Wîkîferheng
kuwiktionary
https://ku.wiktionary.org/wiki/Destp%C3%AAk
MediaWiki 1.47.0-wmf.15
case-sensitive
Medya
Taybet
Gotûbêj
Bikarhêner
Gotûbêja bikarhêner
Wîkîferheng
Gotûbêja Wîkîferhengê
Wêne
Gotûbêja wêneyî
MediaWiki
Gotûbêja MediaWiki
Şablon
Gotûbêja şablonê
Alîkarî
Gotûbêja alîkariyê
Kategorî
Gotûbêja kategoriyê
Pêvek
Gotûbêja pêvekê
Nimînok
Gotûbêja nimînokê
Portal
Gotûbêja portalê
Tewandin
Gotûbêja tewandinê
Jinûvesazî
Gotûbêja jinûvesaziyê
TimedText
TimedText talk
Modul
Gotûbêja modulê
Event
Event talk
kanî
0
3131
6334264
6309582
2026-08-16T23:13:37Z
Wikihez
7344
6334264
wikitext
text/x-wiki
{{bnr-h|Kanî}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{beş|Navdêr}} {{ku-IPA}}
** {{deng|ku|LL-Q36368 (kur)-Dildadil-kanî.wav|dever=Amed}}
* {{beş|Hoker}} {{ku-IPA|"k(h)anî}}
** {{deng|ku|LL-Q36163 (kmr)-Keyomerd-kanî.wav|dever=Qoser}}
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:Nacentemackinac.jpg|thumb|Kaniyek (1)]]
{{navdêr|ku|z=m}}, {{k-ya nerm}}
# [[kun|Kun]]a [[av]] tê re ji [[bin]] [[ax]]ê [[zan|dizê]] anku [[derketin|derdikeve]].
#* {{jêder|ku|jêgirtin=Rûvî da rê, çû ser '''kaniyê''', got:<br>- '''Kaniyê''', '''kaniyê''' ma tu xêra xwe nikarî avê bidî min.|Ji çîrroka gelêrî ''Rûviyê dûvqut''}}
#* {{jêder-kitêb|ku|metna-orîjînal=ku:<br>قمقام و یم بحرن صمصام حسام شیره<br>صحرا فلات و سبسب فیفاء و بدو دشتن<br>ینبوع و عین '''کهنی''' جب و قلیب بیره|sc-orîjînal=Arab|jêgirtin=Qemqam û yem-i beḥr in; ṣemṣam ḥusam şêr e<br>Ṣeḥra felat û sebseb feyfaʾ û bedw-i deşt in<br>Yenbûʿ û ʿeyn '''kehnî'''; cubb û qelîb-i bîr e|nivîskar={{w|Ehmedê Xanî}}|sernav=Nûbihara Biçûkan|sal=1683}}
# [[cih|Cih]]ê ku tiştek pirr lê heye yan lê [[peyda]] dibe.
#: '''''kaniya''' helbest û stranan''
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|كانی}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
|kahnî
|kahnîg
|kanîg
|kanîh
|kehnî
|kehnîh
|kenî
|kenîg
|kenîh
}}
==== Jê ====
* [[çavkanî]]
* [[kanîk]]
* [[serekanî]]
==== Etîmolojî ====
* {{ji|ira-pro||-}}: *'''xa'''- ("kanî, bîr")
:{{ae}}: '''xān''' ("kanî, bîr")
:{{xpr}}: '''x'nyg''' ("kanî")
:{{pal}}: '''x'nyg''' ("kanî")
:sogdî: '''x'xn''' ("kanî")
:{{fa}}: '''xānī''' ("kanî") … {{ir}}
:{{ku}}: '''kehnî''' > '''kanî''' ("kanî")
:{{ckb}}: [[kanî]] ("kanî")
:{{hac}}: '''[[hane]]''' ("kanî")
:{{zza}}: '''[[henî]]''' ("kanî")
{{sa}}: '''[[खानि]] (khāni)'''- ("kanî, bîr")
<font style="font-size:10px">'''Çavkanî:''' [[Şablon:Cheung|Cheung p.404]], [[Şablon:Lubotsky 2|Lubotsky p.9]], [[Şablon:MacKenzie|MacKenzie P.136]] </font> | <font style="font-size:10px">'''Pokorny:''' - </font><br>
<font style="font-size:10px">'''Agadarî: '''Bingehê vê peyvê ne ji proto-hindûewropî ye. Diyar e ku ji zimanek nenas ketiye zimanên arî. Çavkanî: [[Şablon:Lubotsky 2|Lubotsky]]</font>
Herwiha bidin ber tirkiya kevn ''[[kañ]]'', tirkiya niha ''[[kaynak]]''.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|bron}}
* {{Z|sq}}: {{W+|sq|burim|n}}
* {{Z|de}}: {{W+|de|Quelle|m}}
* {{Z|an}}: {{W+|an|fuent}}
* {{Z|aii}}: {{W-|aii|ܢܸܒ݂ܥܵܐ|tr=niwʿa}}
* {{Z|as}}: {{W-|as|উঁহ}}, {{W-|as|পুং}}
* {{Z|ast}}: {{W+|ast|fonte|m}}, {{W+|ast|fuente|m}}
* {{Z|aot}}: {{W-|aot|tyimuk}}
* {{Z|av}}: {{W-|av|ицц}}
* {{Z|az}}: {{W+|az|bulaq}}
* {{Z|eu}}: {{W-|eu|iturri}}
* {{Z|ba}}: {{W-|ba|шишмә}}
* {{Z|be}}: {{W+|be|жарало́|nt}}, {{W-|be|крыні́ца|m}}
* {{Z|bal}}: {{W-|bal|چمگ|tr=cammag}}
* {{Z|bcl}}: {{W-|bcl|burabod}}
* {{Z|ebk}}: {{W-|ebk|fofon}}, {{W-|ebk|ogwor}}
* {{Z|br}}: {{W+|br|eienenn|m}} eien, {{W+|br|andon|m}}, {{W+|br|mammenn|m}}
* {{Z|bg}}: {{W+|bg|и́звор|n}}
* {{Z|cs}}: {{W+|cs|zřídlo|nt}}, {{W+|cs|pramen|n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|泉|tr=quán}}, {{W+|cmn|源泉|tr=yuánquán}}
*: {{Z|cdo}}: {{W-|cdo|泉|tr=ciòng}}
* {{Z|da}}: {{W+|da|kilde|g}}
* {{Z|ar}}: {{W+|ar|عَيْن|n}}, {{W+|ar|يَنْبُوع|n}}
*: {{Z|ary}}: {{W-|ary|عْيْن|n|tr=ʿin}}
* {{Z|hy}}: {{W+|hy|աղբյուր}}, {{W+|hy|ակ}}, {{W+|hy|ակունք}}, {{W+|hy|ակունք}}
* {{Z|eo}}: {{W+|eo|fonto}}
* {{Z|et}}: {{W+|et|allikas}}, {{W-|et|läte}}, {{W-|et|veesilm}}
* {{Z|fa}}: {{W+|fa|چشمه|tr=čašme}}, {{W+|fa|خانی|tr=xâni}}
* {{Z|fi}}: {{W+|fi|lähde}}
* {{Z|frp}}: {{W-|frp|font}}
* {{Z|fro}}: {{W-|fro|fontaine|m}}
* {{Z|fr}}: {{W+|fr|source|m}}
* {{Z|fy}}: {{W-|fy|welle|m}}
* {{Z|gd}}: {{W-|gd|fuaran|n}}
* {{Z|gl}}: {{W+|gl|fonte|m}}, {{W+|gl|manancial|n}}, {{W-|gl|manadeiro|n}}, {{W-|gl|gorgolo|n}}, {{W-|gl|xurre|n}}, {{W-|gl|rieiro|n}}, {{W+|gl|corga|m}}, {{W-|gl|troa|m}}, {{W-|gl|olleiro|n}}
* {{Z|grt}}: {{W-|grt|chimik}}
* {{Z|ka}}: {{W+|ka|წყარო}}
* {{Z|haw}}: {{W-|haw|puna}}
* {{Z|mba}}: {{W-|mba|tubod}}
* {{Z|hi}}: {{W+|hi|चश्मा|n}}
* {{Z|nl}}: {{W+|nl|bron|m}}, {{W+|nl|wel|m}}
* {{Z|he}}: {{W+|he|מעיין|n|tr=ma'ayán|cuda=מַעְיָן}}
* {{Z|io}}: {{W+|io|fonto}}
* {{Z|ilo}}: {{W-|ilo|ubbug}}
* {{Z|id}}: [[mata]] [[air]]
* {{Z|enm}}: {{W-|enm|spryng}}
* {{Z|en}}: {{W+|en|spring}}
* {{Z|ia}}: {{W-|ia|fonte}}
* {{Z|ga}}: {{W-|ga|foinse|m}}
* {{Z|it}}: {{W+|it|fonte|m}}, {{W+|it|sorgente|m}}
* {{Z|is}}: {{W+|is|lind|m}}, {{W+|is|uppspretta|m}}, {{W+|is|brunnur|n}}, {{W-|is|vatnsrás|m}}
* {{Z|ja}}: {{W+|ja|泉|tr=いずみ, izumi}}, {{W+|ja|温泉|tr=おんせん, onsen}}
* {{Z|ksc}}: {{W-|ksc|chagsi}}
* {{Z|kju}}: {{W-|kju|ʔahqʰa}}
* {{Z|ca}}: {{W+|ca|font}}, {{W+|ca|deu|m}}
* {{Z|qu}}: {{W-|qu|pukyu}}
* {{Z|ky}}: {{W+|ky|булак}}
* {{Z|ko}}: {{W+|ko|샘}}
* {{Z|ltg}}: {{W-|ltg|olūts}}
* {{Z|la}}: {{W-|la|scatebra|m}}, {{W-|la|scaturgō|m}}
* {{Z|lv}}: {{W-|lv|avots|n}}
* {{Z|lo}}: {{W-|lo|ນ້ຳພຸ}}
* {{Z|lt}}: {{W-|lt|šaltinis|n}}, {{W-|lt|versmė|m}}, {{W-|lt|verdenė|m}}
* {{Z|knb}}: {{W-|knb|uud}}
* {{Z|lb}}: {{W-|lb|Quell|m}}
* {{Z|mhn}}: {{W-|mhn|prunn|n}}
* {{Z|mk}}: {{W+|mk|извор|n}}, {{W-|mk|вруток|n}}
* {{Z|ms}}: {{W-|ms|mata air}}
* {{Z|mt}}: {{W-|mt|nixxiegħa|m}}, {{W+|mt|għajn|n}}
* {{Z|mnc}}: {{W-|mnc|ᡧᡝᡵᡳ}}
* {{Z|msk}}: {{W-|msk|tobod}}
* {{Z|mi}}: {{W-|mi|puna}}, {{W-|mi|kōmanawa}}
* {{Z|mzn}}: {{W-|mzn|چشمه}}
* {{Z|fit}}: {{W-|fit|kaltio}}
* {{Z|hu}}: {{W+|hu|forrás}}
* {{Z|min}}: {{W-|min|mato aia}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|булаг}}
*: Mongolî: {{W-|mn|ᠪᠤᠯᠠᠭ}}
* {{Z|nci}}: {{W-|nci|āmēyalli}}
* {{Z|nrf}}: {{W-|nrf|r'source|m}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|kilde|n}}
* {{Z|oj}}: {{W-|oj|dakib}}
* {{Z|oc}}: {{W+|oc|font}}
* {{Z|ota}}: {{W-|ota|چشمه|tr=çeşme}}
* {{Z|ug}}: {{W-|ug|بۇلاق}}
* {{Z|uz}}: {{W+|uz|buloq}}, {{W+|uz|chashma}}
* {{Z|pl}}: {{W+|pl|źródło|nt}}, {{W+|pl|zdrój|n}}, {{W+|pl|krynica|m}}
* {{Z|pt}}: {{W+|pt|fonte|m}}, {{W+|pt|manancial|n}}
* {{Z|kk}}: {{W+|kk|бұлақ}}
* {{Z|rom}}: {{W-|rom|zvoro|n}}
* {{Z|ro}}: {{W+|ro|izvor|nt}}
* {{Z|ru}}: {{W+|ru|исто́чник|n}}, {{W+|ru|ключ|n}}, {{W+|ru|родни́к|n}}, {{W+|ru|студене́ц|n}}
* {{Z|sm}}: {{W-|sm|puna}}
* {{Z|sa}}: {{W+|sa|उत्स|n}}
* {{Z|sc}}: {{W-|sc|mitza}}
* {{Z|ceb}}: {{W-|ceb|tubod}}
* {{Z|scn}}: {{W-|scn|fonti|m}}, {{W-|scn|funti|m}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|ѝзвор|n}}
*: Latînî: {{W+|sh|ìzvor|n}}
* {{Z|sk}}: {{W-|sk|žriedlo|nt}}, {{W-|sk|prameň|n}}
* {{Z|sl}}: {{W+|sl|izvír|n}}
* {{Z|es}}: {{W+|es|fuente|m}}, {{W+|es|manantial|n}}, {{W+|es|vertiente}}
* {{Z|sw}}: {{W+|sw|kisima|c7|c8}}
* {{Z|sv}}: {{W+|sv|källa|g}}
* {{Z|shn}}: {{W-|shn|ႁူးၼမ်ႉၸိုမ်း}}
* {{Z|zdj}}: {{W-|zdj|dzitso la madji|c5|c6}}
* {{Z|tg}}: {{W+|tg|чашма}}
* {{Z|tl}}: {{W-|tl|bukal}}, {{W-|tl|batis}}
* {{Z|rif}}: {{W-|rif|tara|m}}
* {{Z|th}}: {{W+|th|น้ำพุ}}
* {{Z|tt}}: {{W+|tt|чишмә}}
* {{Z|tr}}: {{W+|tr|kaynak}}, {{W+|tr|pınar}}, {{W+|tr|bulak}}, {{W+|tr|memba}}
* {{Z|tk}}: {{W-|tk|çeşme}}
* {{Z|bo}}: {{W-|bo|ཆུ་མིག}}
* {{Z|txb}}: {{W-|txb|ālme}}
* {{Z|ifk}}: {{W-|ifk|ob-ob}}, {{W-|ifk|hobwak}}, {{W-|ifk|otbol}}
* {{Z|uga}}: {{W-|uga|𐎐𐎁𐎋}}
* {{Z|ur}}: {{W-|ur|چشمہ}}
* {{Z|uk}}: {{W+|uk|джерело́|nt}}, {{W+|uk|крини́ця|m}}
* {{Z|vec}}: {{W-|vec|fontego|n}}, {{W-|vec|fóntego|n}}
* {{Z|wa}}: {{W+|wa|sourdant|n}}, {{W+|wa|sourdon|n}}, {{W+|wa|sourd|n}}, {{W+|wa|fontinne|m}}
* {{Z|war}}: {{W-|war|burabod}}
* {{Z|cy}}: {{W+|cy|ffynnon|m}}
* {{Z|yai}}: {{W-|yai|чишма}}
* {{Z|yi}}: {{W-|yi|קוואַל|n}}, {{W-|yi|קרעניצע|m}}
* {{Z|el}}: {{W+|el|πηγή|m}}
*: {{Z|grc}}: {{W-|grc|πηγή|m}}, {{W-|grc|κρήνη|m}}
{{werger-bin}}
:{{werger çavkanî|en|spring|beş=Ev beşa Wergerê|oldid=75709724}}
=== Hoker ===
{{hoker|ku}}, {{k-ya req}}
# [[ka]], [[kanê]], li [[kû]] ye, li [[çi]] [[cih]]î ye, li [[kû]] ne
#* {{jêder|ku|jêgirtin='''Kanî''' Sitî, kanî jinmîr û cêrî?<br> Kanîn ew rim, ka huwêzî, şeşperî?|Celadet Elî Bedirxan: ''Bêriya Botan'', helbest}}
==== Werger ====
{{werger-ser}}
{{werger-bin}}
gr2tyqhy1hswspg58u3s6537we38eqx
6334283
6334264
2026-08-17T01:12:13Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir, --Valahiyên nehewce.)
6334283
wikitext
text/x-wiki
{{bnr-h|Kanî}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{beş|Navdêr}} {{ku-IPA}}
** {{deng|ku|LL-Q36368 (kur)-Dildadil-kanî.wav|dever=Amed}}
* {{beş|Hoker}} {{ku-IPA|"k(h)anî}}
** {{deng|ku|LL-Q36163 (kmr)-Keyomerd-kanî.wav|dever=Qoser}}
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:Nacentemackinac.jpg|thumb|Kaniyek (1)]]
{{navdêr|ku|z=m}}, {{k-ya nerm}}
# [[kun|Kuna]] [[av]] tê re ji [[bin]] [[ax]]ê [[zan|dizê]] anku [[derketin|derdikeve]].
#* {{jêder|ku|jêgirtin=Rûvî da rê, çû ser '''kaniyê''', got:<br>- '''Kaniyê''', '''kaniyê''' ma tu xêra xwe nikarî avê bidî min.|Ji çîrroka gelêrî ''Rûviyê dûvqut''}}
#* {{jêder-kitêb|ku|metna-orîjînal=ku:<br>قمقام و یم بحرن صمصام حسام شیره<br>صحرا فلات و سبسب فیفاء و بدو دشتن<br>ینبوع و عین '''کهنی''' جب و قلیب بیره|sc-orîjînal=Arab|jêgirtin=Qemqam û yem-i beḥr in; ṣemṣam ḥusam şêr e<br>Ṣeḥra felat û sebseb feyfaʾ û bedw-i deşt in<br>Yenbûʿ û ʿeyn '''kehnî'''; cubb û qelîb-i bîr e|nivîskar={{w|Ehmedê Xanî}}|sernav=Nûbihara Biçûkan|sal=1683}}
# [[cih|Cihê]] ku tiştek pirr lê heye yan lê [[peyda]] dibe.
#: '''''kaniya''' helbest û stranan''
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|كانی}}
==== Formên alternatîv ====
{{kol3|ku|cure=Herwiha
|kahnî
|kahnîg
|kanîg
|kanîh
|kehnî
|kehnîh
|kenî
|kenîg
|kenîh
}}
==== Jê ====
* [[çavkanî]]
* [[kanîk]]
* [[serekanî]]
==== Etîmolojî ====
* {{ji|ira-pro||-}}: *'''xa'''- ("kanî, bîr")
:{{ae}}: '''xān''' ("kanî, bîr")
:{{xpr}}: '''x'nyg''' ("kanî")
:{{pal}}: '''x'nyg''' ("kanî")
:sogdî: '''x'xn''' ("kanî")
:{{fa}}: '''xānī''' ("kanî") … {{ir}}
:{{ku}}: '''kehnî''' > '''kanî''' ("kanî")
:{{ckb}}: [[kanî]] ("kanî")
:{{hac}}: '''[[hane]]''' ("kanî")
:{{zza}}: '''[[henî]]''' ("kanî")
{{sa}}: '''[[खानि]] (khāni)'''- ("kanî, bîr")
<font style="font-size:10px">'''Çavkanî:''' [[Şablon:Cheung|Cheung p.404]], [[Şablon:Lubotsky 2|Lubotsky p.9]], [[Şablon:MacKenzie|MacKenzie P.136]] </font> | <font style="font-size:10px">'''Pokorny:''' - </font><br>
<font style="font-size:10px">'''Agadarî: '''Bingehê vê peyvê ne ji proto-hindûewropî ye. Diyar e ku ji zimanek nenas ketiye zimanên arî. Çavkanî: [[Şablon:Lubotsky 2|Lubotsky]]</font>
Herwiha bidin ber tirkiya kevn ''[[kañ]]'', tirkiya niha ''[[kaynak]]''.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|bron}}
* {{Z|sq}}: {{W+|sq|burim|n}}
* {{Z|de}}: {{W+|de|Quelle|m}}
* {{Z|an}}: {{W+|an|fuent}}
* {{Z|aii}}: {{W-|aii|ܢܸܒ݂ܥܵܐ|tr=niwʿa}}
* {{Z|as}}: {{W-|as|উঁহ}}, {{W-|as|পুং}}
* {{Z|ast}}: {{W+|ast|fonte|m}}, {{W+|ast|fuente|m}}
* {{Z|aot}}: {{W-|aot|tyimuk}}
* {{Z|av}}: {{W-|av|ицц}}
* {{Z|az}}: {{W+|az|bulaq}}
* {{Z|eu}}: {{W-|eu|iturri}}
* {{Z|ba}}: {{W-|ba|шишмә}}
* {{Z|be}}: {{W+|be|жарало́|nt}}, {{W-|be|крыні́ца|m}}
* {{Z|bal}}: {{W-|bal|چمگ|tr=cammag}}
* {{Z|bcl}}: {{W-|bcl|burabod}}
* {{Z|ebk}}: {{W-|ebk|fofon}}, {{W-|ebk|ogwor}}
* {{Z|br}}: {{W+|br|eienenn|m}} eien, {{W+|br|andon|m}}, {{W+|br|mammenn|m}}
* {{Z|bg}}: {{W+|bg|и́звор|n}}
* {{Z|cs}}: {{W+|cs|zřídlo|nt}}, {{W+|cs|pramen|n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|泉|tr=quán}}, {{W+|cmn|源泉|tr=yuánquán}}
*: {{Z|cdo}}: {{W-|cdo|泉|tr=ciòng}}
* {{Z|da}}: {{W+|da|kilde|g}}
* {{Z|ar}}: {{W+|ar|عَيْن|n}}, {{W+|ar|يَنْبُوع|n}}
*: {{Z|ary}}: {{W-|ary|عْيْن|n|tr=ʿin}}
* {{Z|hy}}: {{W+|hy|աղբյուր}}, {{W+|hy|ակ}}, {{W+|hy|ակունք}}, {{W+|hy|ակունք}}
* {{Z|eo}}: {{W+|eo|fonto}}
* {{Z|et}}: {{W+|et|allikas}}, {{W-|et|läte}}, {{W-|et|veesilm}}
* {{Z|fa}}: {{W+|fa|چشمه|tr=čašme}}, {{W+|fa|خانی|tr=xâni}}
* {{Z|fi}}: {{W+|fi|lähde}}
* {{Z|frp}}: {{W-|frp|font}}
* {{Z|fro}}: {{W-|fro|fontaine|m}}
* {{Z|fr}}: {{W+|fr|source|m}}
* {{Z|fy}}: {{W-|fy|welle|m}}
* {{Z|gd}}: {{W-|gd|fuaran|n}}
* {{Z|gl}}: {{W+|gl|fonte|m}}, {{W+|gl|manancial|n}}, {{W-|gl|manadeiro|n}}, {{W-|gl|gorgolo|n}}, {{W-|gl|xurre|n}}, {{W-|gl|rieiro|n}}, {{W+|gl|corga|m}}, {{W-|gl|troa|m}}, {{W-|gl|olleiro|n}}
* {{Z|grt}}: {{W-|grt|chimik}}
* {{Z|ka}}: {{W+|ka|წყარო}}
* {{Z|haw}}: {{W-|haw|puna}}
* {{Z|mba}}: {{W-|mba|tubod}}
* {{Z|hi}}: {{W+|hi|चश्मा|n}}
* {{Z|nl}}: {{W+|nl|bron|m}}, {{W+|nl|wel|m}}
* {{Z|he}}: {{W+|he|מעיין|n|tr=ma'ayán|cuda=מַעְיָן}}
* {{Z|io}}: {{W+|io|fonto}}
* {{Z|ilo}}: {{W-|ilo|ubbug}}
* {{Z|id}}: [[mata]] [[air]]
* {{Z|enm}}: {{W-|enm|spryng}}
* {{Z|en}}: {{W+|en|spring}}
* {{Z|ia}}: {{W-|ia|fonte}}
* {{Z|ga}}: {{W-|ga|foinse|m}}
* {{Z|it}}: {{W+|it|fonte|m}}, {{W+|it|sorgente|m}}
* {{Z|is}}: {{W+|is|lind|m}}, {{W+|is|uppspretta|m}}, {{W+|is|brunnur|n}}, {{W-|is|vatnsrás|m}}
* {{Z|ja}}: {{W+|ja|泉|tr=いずみ, izumi}}, {{W+|ja|温泉|tr=おんせん, onsen}}
* {{Z|ksc}}: {{W-|ksc|chagsi}}
* {{Z|kju}}: {{W-|kju|ʔahqʰa}}
* {{Z|ca}}: {{W+|ca|font}}, {{W+|ca|deu|m}}
* {{Z|qu}}: {{W-|qu|pukyu}}
* {{Z|ky}}: {{W+|ky|булак}}
* {{Z|ko}}: {{W+|ko|샘}}
* {{Z|ltg}}: {{W-|ltg|olūts}}
* {{Z|la}}: {{W-|la|scatebra|m}}, {{W-|la|scaturgō|m}}
* {{Z|lv}}: {{W-|lv|avots|n}}
* {{Z|lo}}: {{W-|lo|ນ້ຳພຸ}}
* {{Z|lt}}: {{W-|lt|šaltinis|n}}, {{W-|lt|versmė|m}}, {{W-|lt|verdenė|m}}
* {{Z|knb}}: {{W-|knb|uud}}
* {{Z|lb}}: {{W-|lb|Quell|m}}
* {{Z|mhn}}: {{W-|mhn|prunn|n}}
* {{Z|mk}}: {{W+|mk|извор|n}}, {{W-|mk|вруток|n}}
* {{Z|ms}}: {{W-|ms|mata air}}
* {{Z|mt}}: {{W-|mt|nixxiegħa|m}}, {{W+|mt|għajn|n}}
* {{Z|mnc}}: {{W-|mnc|ᡧᡝᡵᡳ}}
* {{Z|msk}}: {{W-|msk|tobod}}
* {{Z|mi}}: {{W-|mi|puna}}, {{W-|mi|kōmanawa}}
* {{Z|mzn}}: {{W-|mzn|چشمه}}
* {{Z|fit}}: {{W-|fit|kaltio}}
* {{Z|hu}}: {{W+|hu|forrás}}
* {{Z|min}}: {{W-|min|mato aia}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|булаг}}
*: Mongolî: {{W-|mn|ᠪᠤᠯᠠᠭ}}
* {{Z|nci}}: {{W-|nci|āmēyalli}}
* {{Z|nrf}}: {{W-|nrf|r'source|m}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|kilde|n}}
* {{Z|oj}}: {{W-|oj|dakib}}
* {{Z|oc}}: {{W+|oc|font}}
* {{Z|ota}}: {{W-|ota|چشمه|tr=çeşme}}
* {{Z|ug}}: {{W-|ug|بۇلاق}}
* {{Z|uz}}: {{W+|uz|buloq}}, {{W+|uz|chashma}}
* {{Z|pl}}: {{W+|pl|źródło|nt}}, {{W+|pl|zdrój|n}}, {{W+|pl|krynica|m}}
* {{Z|pt}}: {{W+|pt|fonte|m}}, {{W+|pt|manancial|n}}
* {{Z|kk}}: {{W+|kk|бұлақ}}
* {{Z|rom}}: {{W-|rom|zvoro|n}}
* {{Z|ro}}: {{W+|ro|izvor|nt}}
* {{Z|ru}}: {{W+|ru|исто́чник|n}}, {{W+|ru|ключ|n}}, {{W+|ru|родни́к|n}}, {{W+|ru|студене́ц|n}}
* {{Z|sm}}: {{W-|sm|puna}}
* {{Z|sa}}: {{W+|sa|उत्स|n}}
* {{Z|sc}}: {{W-|sc|mitza}}
* {{Z|ceb}}: {{W-|ceb|tubod}}
* {{Z|scn}}: {{W-|scn|fonti|m}}, {{W-|scn|funti|m}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|ѝзвор|n}}
*: Latînî: {{W+|sh|ìzvor|n}}
* {{Z|sk}}: {{W-|sk|žriedlo|nt}}, {{W-|sk|prameň|n}}
* {{Z|sl}}: {{W+|sl|izvír|n}}
* {{Z|es}}: {{W+|es|fuente|m}}, {{W+|es|manantial|n}}, {{W+|es|vertiente}}
* {{Z|sw}}: {{W+|sw|kisima|c7|c8}}
* {{Z|sv}}: {{W+|sv|källa|g}}
* {{Z|shn}}: {{W-|shn|ႁူးၼမ်ႉၸိုမ်း}}
* {{Z|zdj}}: {{W-|zdj|dzitso la madji|c5|c6}}
* {{Z|tg}}: {{W+|tg|чашма}}
* {{Z|tl}}: {{W-|tl|bukal}}, {{W-|tl|batis}}
* {{Z|rif}}: {{W-|rif|tara|m}}
* {{Z|th}}: {{W+|th|น้ำพุ}}
* {{Z|tt}}: {{W+|tt|чишмә}}
* {{Z|tr}}: {{W+|tr|kaynak}}, {{W+|tr|pınar}}, {{W+|tr|bulak}}, {{W+|tr|memba}}
* {{Z|tk}}: {{W-|tk|çeşme}}
* {{Z|bo}}: {{W-|bo|ཆུ་མིག}}
* {{Z|txb}}: {{W-|txb|ālme}}
* {{Z|ifk}}: {{W-|ifk|ob-ob}}, {{W-|ifk|hobwak}}, {{W-|ifk|otbol}}
* {{Z|uga}}: {{W-|uga|𐎐𐎁𐎋}}
* {{Z|ur}}: {{W-|ur|چشمہ}}
* {{Z|uk}}: {{W+|uk|джерело́|nt}}, {{W+|uk|крини́ця|m}}
* {{Z|vec}}: {{W-|vec|fontego|n}}, {{W-|vec|fóntego|n}}
* {{Z|wa}}: {{W+|wa|sourdant|n}}, {{W+|wa|sourdon|n}}, {{W+|wa|sourd|n}}, {{W+|wa|fontinne|m}}
* {{Z|war}}: {{W-|war|burabod}}
* {{Z|cy}}: {{W+|cy|ffynnon|m}}
* {{Z|yai}}: {{W-|yai|чишма}}
* {{Z|yi}}: {{W-|yi|קוואַל|n}}, {{W-|yi|קרעניצע|m}}
* {{Z|el}}: {{W+|el|πηγή|m}}
*: {{Z|grc}}: {{W-|grc|πηγή|m}}, {{W-|grc|κρήνη|m}}
{{werger-bin}}
:{{werger çavkanî|en|spring|beş=Ev beşa Wergerê|oldid=75709724}}
=== Hoker ===
{{hoker|ku}}, {{k-ya req}}
# [[ka]], [[kanê]], li [[kû]] ye, li [[çi]] [[cih]]î ye, li [[kû]] ne
#* {{jêder|ku|jêgirtin='''Kanî''' Sitî, kanî jinmîr û cêrî?<br> Kanîn ew rim, ka huwêzî, şeşperî?|Celadet Elî Bedirxan: ''Bêriya Botan'', helbest}}
==== Werger ====
{{werger-ser}}
{{werger-bin}}
a27vjk5kkcdrzw2d2u1qmse7c6932sw
me
0
3469
6334262
6327212
2026-08-16T23:04:40Z
Wikihez
7344
/* Formekê lêkerê */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334262
wikitext
text/x-wiki
{{bnr-h|mê|me-}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163_(kmr)-Key Mîrza-me.wav|dever=Wan}}
* {{ku-kîte}}
=== Cînav ===
{{ku-zayend|MNY=min|MNP=me}}
{{cînav|ku}}
# ''Forma [[tewandî]] ji [[cînav]]ê'' '''[[em]]'''; [[min]] û yên [[wek]] [[min]].
#: {{mk|ku|Ew '''me''' dibînin.}}
#: {{mk|ku|'''Me''' wisa negotiye.}}
#: {{mk|ku|Li gel '''me''' werin.}}
#* {{jêder-kitêb|ku-pro|nekat=1|amadekar=D.N. Mackenzie|sernav=The Language of the Medians |nivîskar={{w|Matenadaran MS 7117|ziman=en}}|sal=~1430-1446|sala weşandinê=1959|jêgirtin=Pakəž xodé, pakəž záhm, pakəž vémark, kóy hat'ì xač'é, əškə́rma. Rahmát'ē '''ma'''.|tr=Pakij xudê, pakij zehm, pakij vêmerg, koy hatî xaçê, ijkir me rehmetê '''me'''<ref name="Bor2016">{{kovar|url=https://dergipark.org.tr/tr/download/article-file/416578|kovar=Nûbihar Akademî|nivîskar=D.N. Mackenzie|sernav=Zimanê Medan|sal=2016|terciman=İbrahim Bor|hejmar=4|rûpel=116}}</ref>|werger=Pakê Xweda, Pakê Qadir, Pakê Nemir, mirina di xaçê de ku hat, Xweda bi '''me''' bê rehmê<ref name="Bor2016"/>}}
#* {{jêder|ku|{{Jêgirtin/Mela Hisênê Bateyî/Mewlûda Kurdî}}|jêgirtin=Ew Xudayê malikê mulkê 'ezîm<br />Daye '''me''' mîrasa Qur'ana Kerîm<br />Dînê '''me''' kir kamil û ni''''me'''t-temam}}
#* {{J:ku:Kurdistan:1898|sernav=Bîsmîllahîrrehmanîrrehîm|nivîskar=Mîqdad Mîdhet Bedirxan|sal=1898|hj=1|r=1||cild=1|jg=Sed hezar şikir û hemd ji Xwedê teala re, em Musulman xelq kirin û zanîna ilm û marîfetê re hiş û zeka da '''me'''.}}
==== Têkildar ====
{{cînav/ku}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Bide ber ====
* [[em]]
==== Werger ====
{{werger-ser|wek [[obje]]}}
* {{Z|de}}: {{W|de|uns}}
* {{Z|ar}}: {{W|ar|نا|guman=erê}}
* {{Z|fa}}: {{W|fa|ما}}, {{W|fa|انسداد|guman=erê}}
* {{Z|fi}}: {{W|fi|meidät}}, {{W|fi|meitä}}
* {{Z|fr}}: {{W|fr|nous}}
* {{Z|en}}: {{W|en|us}}
* {{Z|es}}: {{W|es|nos}}
* {{Z|sv}}: {{W|sv|oss}}
* {{Z|tr}}: {{W|tr|bizi}}
{{werger-bin}}
{{werger-ser|wek [[subje]]}}
* {{Z|de}}: {{W|de|wir}}
* {{Z|fi}}: {{W|fi|me}}
* {{Z|fr}}: {{W|fr|nous}}
* {{Z|en}}: {{W|en|we}}
* {{Z|it}}: {{W|it|noi}}
* {{Z|es}}: [[nosotros]] ''nêr & giştî'', {{W|es|nosotras}} {{mê}}
* {{Z|sv}}: {{W|sv|vi}}
* {{Z|tr}}: {{W|tr|biz}}
{{werger-bin}}
=== Formeke lêkerê ===
{{lêker|ku|form=erê}}
# {{maneid|ku|form}} Forma [[dema niho]] ji [[lêker]]a '''[[bûn]]''' bo kesa/ê yekem ya / yê [[yekjimar]] dema [[peyv]]a [[berî]] wê [[vokal]] be.
#: {{mk|ku|Ez mamoste '''me'''.}}
#: {{mk|ku|Ez ne swêdî '''me'''.}}
==== Formên alternatîv ====
* {{mj|devokî}} {{g|ku|mim|id=form}}
* {{mj|biştî dengdarekê}} {{g|ku|im|id=form}}
==== Bide ber ====
{{mj|formên lêkera bûn}}:
* {{mj|piştî dengdarekê}}: {{g|ku|im|id=form}}, {{g|ku|î|id=form}}, {{g|ku|e|id=form}}, {{g|ku|in|id=form}}, {{g|ku|în||id=form}} ''(devok)''
* {{mj|biştî bêdengekê}}: {{g|ku|me|id=form}}, {{g|ku|yî|id=form}}, {{g|ku|ye|id=form}}, {{g|ku|ne|id=form}}
==== Binêre herwiha ====
* [[-me]], [[-im]]
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
== {{ziman|sdh}} ==
=== Cînav ===
{{cînav|sdh}}
# {{sdh-Arab|مە||w=ez, min}}
==== Formên alternatîv ====
* {{g|sdh|mi|sc=Latn}}
== {{ziman|fi}} ==
=== Bilêvkirin ===
* {{deng|fi|LL-Q1412 (fin)-Susannaanas-me.wav|Deng|dever=Fînlenda}}
* {{deng|fi|LL-Q1412 (fin)-Nurtsio-me.wav|Deng|dever=Helsînkî, Fînlenda}}
* {{deng|fi|LL-Q1412 (fin)-Seqv-me.wav|Deng|dever=Yekîtiya Ewropayê, navneteweyî}}
=== Cînav ===
{{cînav|fi}}
# [[em]], [[ez]] û yên wek [[min]]
#: {{mk|fi|Me sanomme.|Em dibêjin.|rêz=1}}
# me, [[min]] û yên wek [[min]]
#: {{mk|fi|Me sanoimme.|Me got.|rêz=1}}
==== Jê ====
{{kol3|fi
|meidän
|meidät
|meitä
}}
==== Bide ber ====
{{kol3|fi
|minä
|sinä
|hän
}}
== {{ziman|en}} ==
=== Bilêvkirin ===
:{{ku-tîp|mî}}
* {{deng|en|en-us-me.ogg|dever=DYA}}
* {{deng|en|en-au-me.ogg|dever=Awistralya}}
* {{deng|en|LL-Q1860 (eng)-Nattes à chat-me.wav|Deng|dever=}}
* {{deng|en|LL-Q1860 (eng)-Back ache-me.wav|Deng|dever=London,QY}}
* {{deng|en|LL-Q1860 (eng)-Simplificationalizer-me.wav|Deng|dever=New Jersey, DYA}}
=== Cînav ===
{{cînav|en}}
# [[min]]
#: {{mk|en|She knows '''me'''.|Ew min dinase.|rêz=1}}
# [[ez]]
#: {{mk|en|She saw '''me'''.|Wê ez dîtim.|rêz=1}}
==== Têkildar ====
* {{g|en|I}} {{mj|îngilîzî}}
==== Bide ber ====
{{kol3|en
|you
|she
|they
}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# navê tîpa şazdemîn herfa alfabeya tirkî, [[M]]
== {{ziman|la}} ==
=== Lêker ===
{{lêker|la}}
# me, cînavkê rewşên vegerok û ravekirî bo kesên yekem yê pirjimar
aukm9xxt4jga54ycobqlgichkgg5ufk
6334279
6334262
2026-08-16T23:41:13Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334279
wikitext
text/x-wiki
{{bnr-h|mê|me-}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163_(kmr)-Key Mîrza-me.wav|dever=Wan}}
* {{ku-kîte}}
=== Cînav ===
{{ku-zayend|MNY=min|MNP=me}}
{{cînav|ku}}
# ''Forma [[tewandî]] ji [[cînav]]ê'' '''[[em]]'''; [[min]] û yên [[wek]] [[min]].
#: {{mk|ku|Ew '''me''' dibînin.}}
#: {{mk|ku|'''Me''' wisa negotiye.}}
#: {{mk|ku|Li gel '''me''' werin.}}
#* {{jêder-kitêb|ku-pro|nekat=1|amadekar=D.N. Mackenzie|sernav=The Language of the Medians |nivîskar={{w|Matenadaran MS 7117|ziman=en}}|sal=~1430-1446|sala weşandinê=1959|jêgirtin=Pakəž xodé, pakəž záhm, pakəž vémark, kóy hat'ì xač'é, əškə́rma. Rahmát'ē '''ma'''.|tr=Pakij xudê, pakij zehm, pakij vêmerg, koy hatî xaçê, ijkir me rehmetê '''me'''<ref name="Bor2016">{{kovar|url=https://dergipark.org.tr/tr/download/article-file/416578|kovar=Nûbihar Akademî|nivîskar=D.N. Mackenzie|sernav=Zimanê Medan|sal=2016|terciman=İbrahim Bor|hejmar=4|rûpel=116}}</ref>|werger=Pakê Xweda, Pakê Qadir, Pakê Nemir, mirina di xaçê de ku hat, Xweda bi '''me''' bê rehmê<ref name="Bor2016"/>}}
#* {{jêder|ku|{{Jêgirtin/Mela Hisênê Bateyî/Mewlûda Kurdî}}|jêgirtin=Ew Xudayê malikê mulkê 'ezîm<br />Daye '''me''' mîrasa Qur'ana Kerîm<br />Dînê '''me''' kir kamil û ni''''me'''t-temam}}
#* {{J:ku:Kurdistan:1898|sernav=Bîsmîllahîrrehmanîrrehîm|nivîskar=Mîqdad Mîdhet Bedirxan|sal=1898|hj=1|r=1||cild=1|jg=Sed hezar şikir û hemd ji Xwedê teala re, em Musulman xelq kirin û zanîna ilm û marîfetê re hiş û zeka da '''me'''.}}
==== Têkildar ====
{{cînav/ku}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Bide ber ====
* [[em]]
==== Werger ====
{{werger-ser|wek [[obje]]}}
* {{Z|de}}: {{W|de|uns}}
* {{Z|ar}}: {{W|ar|نا|guman=erê}}
* {{Z|fa}}: {{W|fa|ما}}, {{W|fa|انسداد|guman=erê}}
* {{Z|fi}}: {{W|fi|meidät}}, {{W|fi|meitä}}
* {{Z|fr}}: {{W|fr|nous}}
* {{Z|en}}: {{W|en|us}}
* {{Z|es}}: {{W|es|nos}}
* {{Z|sv}}: {{W|sv|oss}}
* {{Z|tr}}: {{W|tr|bizi}}
{{werger-bin}}
{{werger-ser|wek [[subje]]}}
* {{Z|de}}: {{W|de|wir}}
* {{Z|fi}}: {{W|fi|me}}
* {{Z|fr}}: {{W|fr|nous}}
* {{Z|en}}: {{W|en|we}}
* {{Z|it}}: {{W|it|noi}}
* {{Z|es}}: [[nosotros]] ''nêr & giştî'', {{W|es|nosotras}} {{mê}}
* {{Z|sv}}: {{W|sv|vi}}
* {{Z|tr}}: {{W|tr|biz}}
{{werger-bin}}
=== Formeke lêkerê ===
{{lêker|ku|form=erê}}
# {{maneid|ku|form}} Forma [[dema niho]] ji [[lêker]]a '''[[bûn]]''' bo kesa/ê yekem ya / yê [[yekjimar]] dema [[peyv]]a [[berî]] wê [[vokal]] be.
#: {{mk|ku|Ez mamoste '''me'''.}}
#: {{mk|ku|Ez ne swêdî '''me'''.}}
==== Formên alternatîv ====
* {{mj|devokî}} {{g|ku|mim|id=form}}
* {{mj|biştî dengdarekê}} {{g|ku|im|id=form}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Bide ber ====
{{mj|formên lêkera bûn}}:
* {{mj|piştî dengdarekê}}: {{g|ku|im|id=form}}, {{g|ku|î|id=form}}, {{g|ku|e|id=form}}, {{g|ku|in|id=form}}, {{g|ku|în||id=form}} ''(devok)''
* {{mj|biştî bêdengekê}}: {{g|ku|me|id=form}}, {{g|ku|yî|id=form}}, {{g|ku|ye|id=form}}, {{g|ku|ne|id=form}}
==== Herwiha binêre ====
* [[-me]], [[-im]]
==== Werger ====
{{werger-ser}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
== {{ziman|sdh}} ==
=== Cînav ===
{{cînav|sdh}}
# {{sdh-Arab|مە||w=ez, min}}
==== Formên alternatîv ====
* {{g|sdh|mi|sc=Latn}}
== {{ziman|fi}} ==
=== Bilêvkirin ===
* {{deng|fi|LL-Q1412 (fin)-Susannaanas-me.wav|Deng|dever=Fînlenda}}
* {{deng|fi|LL-Q1412 (fin)-Nurtsio-me.wav|Deng|dever=Helsînkî, Fînlenda}}
* {{deng|fi|LL-Q1412 (fin)-Seqv-me.wav|Deng|dever=Yekîtiya Ewropayê, navneteweyî}}
=== Cînav ===
{{cînav|fi}}
# [[em]], [[ez]] û yên wek [[min]]
#: {{mk|fi|Me sanomme.|Em dibêjin.|rêz=1}}
# me, [[min]] û yên wek [[min]]
#: {{mk|fi|Me sanoimme.|Me got.|rêz=1}}
==== Jê ====
{{kol3|fi
|meidän
|meidät
|meitä
}}
==== Bide ber ====
{{kol3|fi
|minä
|sinä
|hän
}}
== {{ziman|en}} ==
=== Bilêvkirin ===
:{{ku-tîp|mî}}
* {{deng|en|en-us-me.ogg|dever=DYA}}
* {{deng|en|en-au-me.ogg|dever=Awistralya}}
* {{deng|en|LL-Q1860 (eng)-Nattes à chat-me.wav|Deng|dever=}}
* {{deng|en|LL-Q1860 (eng)-Back ache-me.wav|Deng|dever=London,QY}}
* {{deng|en|LL-Q1860 (eng)-Simplificationalizer-me.wav|Deng|dever=New Jersey, DYA}}
=== Cînav ===
{{cînav|en}}
# [[min]]
#: {{mk|en|She knows '''me'''.|Ew min dinase.|rêz=1}}
# [[ez]]
#: {{mk|en|She saw '''me'''.|Wê ez dîtim.|rêz=1}}
==== Têkildar ====
* {{g|en|I}} {{mj|îngilîzî}}
==== Bide ber ====
{{kol3|en
|you
|she
|they
}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# navê tîpa şazdemîn herfa alfabeya tirkî, [[M]]
== {{ziman|la}} ==
=== Lêker ===
{{lêker|la}}
# me, cînavkê rewşên vegerok û ravekirî bo kesên yekem yê pirjimar
mfrfb6o7tk6n4yohm9eaqxws3p9ouvd
li
0
4265
6334164
6325577
2026-08-16T17:51:53Z
Wikihez
7344
/* Pêşdaçek */
6334164
wikitext
text/x-wiki
{{bnr-h|Li|LI}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-li.wav|dever=Bidlîs}}
* {{ku-kîte}}
=== Pêşdaçek ===
{{pêşdaçek|ku}}
# {{înm|[[daçek|Daçekek]] e bo diyarkirina [[cih]]ê tiştekî.}}
#: {{nimûne|ku|li Kurdistanê}}
#: {{nimûne|ku|li [[herêm]]ê}}
#: {{nimûne|ku|li gundekî}}
#: {{nimûne|ku|li malê}}
#* {{jêder-kovar|ku|jêgirtin=Lê mixabin '''li''' Qefqasya, Rûsya û Asya Navîn rewşa xwendin û nivîsandina zimanê Kurdî pir kêm e. Di vî warî de rola saziya Berbangê, kovara Nûbarê û rojnameya Jiyana Kurd gelekî girîng e. Lê diyar e ev xebat têrê nake.|sal=2021|nivîskar=Salihê Kevirbirî|url=http://books.google.com.tr/books?id=qFw3EAAAQBAJ&pg=PA50&hl=&cd=6&source=gbs_api|sernav=Hosteykî Edebiyata Kurdî: Eliyê Evdirehmanê Momîn|kovar=Nûbihar|hejmar=150|rûpel=50}}
#* {{jêder-kitêb|ku|jêgirtin='''Li''' Swêregê û Diyarbekîrê em Suat Karataş û Yahya Mehmedoglu re alîkarî dikin, em dixwazin ew her du bibin reîsê belediyê.|nivîskar=Paşa Uzun|sernav=Xalê min Ferîd|url=https://bnk.institutkurde.org/images/pdf/RFTG9DQA76.pdf|weşanxane=Weşanxaneyê Vateyî|sal=2006|rûpel=59}}
# {{înm|Daçekek e bo diyarkirina [[dem]]ê.}}
#: {{nimûne|ku|li sala 2000ê}}
#: {{nimûne|ku|li biharê}}
#: {{nimûne|ku|li êvarê}}
==== Formên alternatîv ====
* {{g|ku|l'}}
* {{g|ku|l}}
* {{g|ku|le}} (sùrçî)
* {{g|ku|la}} (nurheqî)
==== Bi alfabeyên din ====
* {{ku-ar|ل}}
* {{ku-kr|ль}}
==== Bikaranîn ====
{{agahdarî}} Daçeka "li" diyar dike ku tiştek li cihekî ye, daçeka "di ... de" diyar dike ku tiştek di cihekî de ye: "Li Kurdistanê", "li Diyarbekirê", "li meydanê" lê "di kitêbê de", "di qazanê de"(ne "*di Kurdistanê de", ne "di Diyarbekirê de", ne "*di meydanê de" lê herwiha ne "*li kitêbê" û ne "*li qazanê".)
Dema ev daçek serbixwe tê bikaranîn (ango ti [[nomîn]] nadin pey wê), ew dibe '''[[lê]]'''; ''lê kirin'': li wê kirin; li wî kirin; li wan kirin
==== Jê ====
{{kol3|ku|cure=Jê
|li alî
|li ber
|li bin
|li der
|li hêlê
|li hindir
|li jêr
|li jor
|li nav
|li rex
|li ser
|li tenişt
}}
==== Herwiha binêre ====
* {{g|ku|bi}}
* {{g|ku|di}}
* {{g|ku|ji}}
==== Etîmolojî ====
Hevreha {{hv|ckb|له|tr=[[le]]}}, hewramî [[ne]], [[hexamenişî]] ''ena'', ji {{mîr|ine-pro|ku|*en}} ([[li]], [[tê de]], [[di ... de]]) û wisa hevreha îngilîzî, almanî, latînî û gelek zimanên din [[in]], fransî û {{hv|es|en}}, {{hv|grc|ἐν|tr=en}}, {{hv|sq|në}}, {{hv|hy|ի|tr=i}}, danmarkî, norwecî û {{hv|sv|i}}, {{hv|lt|į}}, îrlendiya kevn [[i]], weylsî [[yn]]... Bo guherîna N > L bide ber: [[nimêj]] > [[limêj]], [[nivîn]] > [[livîn]].
Heke ne ji {{ji|ine-pro|ku|-}} be, dikare ji [[al]] / [[la]] (rex) be. Dîsa dikare ji {{ji|ar|ku|على}} ('ela: li ser) jî be. Herwiha bide ber {{hv|tr|-la}} / [[-le]] (bi, li gel, bi ... re).
==== Werger ====
{{werger-ser}}
* {{Z|sq}}: {{W-|sq|në}}
* {{Z|de}}: {{W+|de|an}}, {{W+|de|auf}}, {{W+|de|bei}}, {{W+|de|in}}, {{W+|de|zu}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܒܓܘ|tr=bə-ḡaw}}
*: {{Z|arc}}: {{W-|arc|גַּו|tr=gaw|sc=Hebr}}
* {{Z|be}}: {{W-|be|у}}, {{W-|be|ў}}, {{W-|be|пры}}, {{W-|be|каля́}}
* {{Z|br}}: {{W+|br|e}}
* {{Z|bg}}: {{W+|bg|при}}, {{W+|bg|до}}, {{W+|bg|на}}, {{W+|bg|у}}
* {{Z|my}}: {{W+|my|မှာ}}, {{W+|my|၌}}
* {{Z|cs}}: {{W+|cs|u}}, {{W+|cs|při}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|喺}}, {{W-|yue|在}}
*: {{Z|cmn}}: {{W+|cmn|在}}
* {{Z|da}}: {{W+|da|på}}
* {{Z|ar}}: {{W+|ar|فِي}}
*: {{Z|ary}}: {{W-|ary|في|tr=fi}}, {{W-|ary|فـ|tr=f-}}
*: {{Z|aeb}}: {{W-|aeb|في|tr=fi}}
* {{Z|eo}}: {{W+|eo|ĉe}}
* {{Z|fa}}: {{W+|fa|در|tr=dar}}
* {{Z|fo}}: {{W+|fo|í}}, {{W+|fo|á}}, {{W+|fo|hjá}}, {{W-|fo|til}}
* {{Z|fi}}: {{W+|fi|-lla}}, {{W+|fi|-llä}}, {{W+|fi|-ssa}}, {{W+|fi|-ssä}}, {{W+|fi|luona}}, {{W+|fi|tykönä}}
* {{Z|fr}}: {{W+|fr|à}}, {{W+|fr|en}}
* {{Z|gl}}: {{W+|gl|en}}
* {{Z|got}}: {{W-|got|𐌰𐍄}}
* {{Z|hi}}: {{W+|hi|पर}}, {{W+|hi|में}}, {{W+|hi|से}}
* {{Z|nl}}: {{W+|nl|op}}, {{W+|nl|bij}}
* {{Z|he}}: {{W-|he|אֵצֶל|tr=etsél}}
* {{Z|io}}: {{W+|io|ye}}
* {{Z|en}}: {{W+|en|in}}, {{W+|en|at}}
* {{Z|ia}}: {{W+|ia|a}}, {{W+|ia|in}}
* {{Z|ga}}: {{W-|ga|ag}}
* {{Z|it}}: {{W+|it|a}}, {{W+|it|in}}
* {{Z|is}}: {{W+|is|í}}, {{W+|is|á}}
* {{Z|ja}}: {{W+|ja|で|tr=...de|cuda=...で}}, {{W+|ja|に|tr=...ni|cuda=... に}}
* {{Z|ca}}: {{W+|ca|a}}
* {{Z|ko}}: {{W+|ko|에서|cuda=...에서}}, {{W+|ko|에|cuda=...에}}
* {{Z|ltg}}: {{W-|ltg|pi}}
* Latin: ''[[dative case]]'', {{W+|la|in}}
* {{Z|lv}}: {{W+|lv|pie}}
* Lithuanian: ''[[locative case]]'', {{W+|lt|pas}}
* {{Z|lo}}: {{W-|lo|ທີ່}}
* {{Z|mk}}: {{W-|mk|во}}, {{W-|mk|на}}, {{W-|mk|кај}}, {{W-|mk|при}}
* {{Z|ms}}: {{W+|ms|di}}, {{W+|ms|dekat}}
* {{Z|mnc}}: {{W-|mnc|ᡩᡝ}}
* {{Z|mi}}: {{W+|mi|i}}, {{W-|mi|kei}}, {{W-|mi|hei}}, {{W-|mi|a}}
* {{Z|hu}}: {{W+|hu|-n}}, {{W+|hu|-on}}, {{W+|hu|-en}}, {{W+|hu|-ön}}, {{W+|hu|-nál}}, {{W+|hu|-nél}}
* {{Z|mnw}}: {{W-|mnw|ပ္ဍဲ|tr=pḍay}}
* {{Z|nap}}: {{W-|nap|a}}
* {{Z|nv}}: {{W-|nv|-di}}, {{W-|nv|-gi}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|på}}
*: {{Z|nn}}: {{W+|nn|på}}
* {{Z|pox}}: {{W-|pox|ai̯}}, {{W-|pox|au̯}}
* {{Z|pl}}: {{W+|pl|w}}, {{W+|pl|u}}, {{W+|pl|przy}}
* {{Z|pt}}: {{W+|pt|em}}, {{W+|pt|por}}, {{W+|pt|a}}
* {{Z|rap}}: {{W-|rap|i}}
* {{Z|rom}}: {{W-|rom|kaj}}
* {{Z|ro}}: {{W+|ro|la}}
* {{Z|ru}}: {{W+|ru|в}}, {{W+|ru|на}}, {{W+|ru|у}}, {{W-|ru|во́зле}}
* {{Z|sk}}: {{W+|sk|v}}, {{W-|sk|pri}}, {{W+|sk|na}}
* {{Z|sl}}: {{W+|sl|pri}}, {{W+|sl|na}}, {{W+|sl|ob}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|wu}}, {{W-|dsb|pśi}}
* {{Z|es}}: {{W+|es|en}}
* {{Z|su}}: {{W-|su|dina}}
* {{Z|sw}}: {{W-|sw|karibu na}}
* {{Z|sv}}: {{W+|sv|på}}, {{W+|sv|vid}}
* {{Z|th}}: {{W+|th|ที่}}
* {{Z|te}}: {{W+|te|వద్ద}}
* {{Z|tr}}: {{W+|tr|-da}}, {{W+|tr|-de}}
* {{Z|bo}}: {{W-|bo|ལ}}
* {{Z|tpi}}: {{W-|tpi|long}}
* {{Z|uk}}: {{W+|uk|у}}, {{W+|uk|в}}, {{W+|uk|при}}, {{W-|uk|біля́}}
* {{Z|vi}}: {{W-|vi|ở tại}}, {{W+|vi|lúc}}, {{W+|vi|vào}}, {{W+|vi|ở}}
* {{Z|vo}}: {{W+|vo|pö}}
* {{Z|wa}}: {{W+|wa|a}}
* Welsh: {{sense|in}} {{W+|cy|yn}}, {{W+|cy|mewn}}; {{sense|near}} {{W-|cy|wrth}}, {{W+|cy|ger}}
* {{Z|grc}}: {{W-|grc|ἐν}}, {{W-|grc|εἰς}}
* {{Z|el}}: {{W+|el|στον|n}}, {{W-|el|στη|m}}, {{W+|el|στο|nt}}, {{W-|el|στους|n-p}}, {{W+|el|στις|m-p}}, {{W+|el|στα|nt-p}}
* {{Z|zea}}: {{W-|zea|bie}}, {{W-|zea|in}}, {{W-|zea|nessens}}
{{werger-bin}}
:{{werger çavkanî|en|at|beş=Ev qismê Wergerê|oldid=75667425}}
== {{ziman|eo}} ==
=== Bilêvkirin ===
{{eo-IPA}}
* {{deng|eo|LL-Q143 (epo)-Robin van der Vliet-li.wav|Deng|dever=Wijchen, Holenda}}
* {{deng|eo|LL-Q143 (epo)-NMaia-li.wav|Deng|dever=Brezîl}}
* {{deng|eo|LL-Q143 (epo)-Lepticed7-li.wav|Deng|dever=Toulouse, Fransa}}
* {{deng|eo|LL-Q143 (epo)-Poslovitch-li.wav|Deng|dever=Vosges, Fransa}}
=== Cînav ===
{{eo-tewîn-cînav}}
{{cînav|eo}}
# (mêr) [[ew]], [[wî]]
#: {{mk|eo|'''Li''' amas vin.|'''Ew''' ji te hez dike.|rêz=1}}
#: {{mk|eo|'''Li''' skribis.|'''Wî''' nivîsand.|rêz=1}}
==== Jê ====
* {{g|eo|ili}}
==== Etîmolojî ====
Deyn ji {{ji|es|eo|le}}, {{ji|it|eo|lui}}, {{ji|fr|eo|lui}}.
==== Herwiha binêre ====
{{eo-cînav}}
6fb02kgpdq1famgdg66ammcm05rbiku
6334185
6334164
2026-08-16T18:41:14Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334185
wikitext
text/x-wiki
{{bnr-h|Li|LI}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{deng|ku|LL-Q36163 (kmr)-Ebulfîda-li.wav|dever=Bidlîs}}
* {{ku-kîte}}
=== Pêşdaçek ===
{{pêşdaçek|ku}}
# {{înm|[[daçek|Daçekek]] e bo diyarkirina [[cih]]ê tiştekî.}}
#: {{nimûne|ku|li Kurdistanê}}
#: {{nimûne|ku|li [[herêm]]ê}}
#: {{nimûne|ku|li gundekî}}
#: {{nimûne|ku|li malê}}
#* {{jêder-kovar|ku|jêgirtin=Lê mixabin '''li''' Qefqasya, Rûsya û Asya Navîn rewşa xwendin û nivîsandina zimanê Kurdî pir kêm e. Di vî warî de rola saziya Berbangê, kovara Nûbarê û rojnameya Jiyana Kurd gelekî girîng e. Lê diyar e ev xebat têrê nake.|sal=2021|nivîskar=Salihê Kevirbirî|url=http://books.google.com.tr/books?id=qFw3EAAAQBAJ&pg=PA50&hl=&cd=6&source=gbs_api|sernav=Hosteykî Edebiyata Kurdî: Eliyê Evdirehmanê Momîn|kovar=Nûbihar|hejmar=150|rûpel=50}}
#* {{jêder-kitêb|ku|jêgirtin='''Li''' Swêregê û Diyarbekîrê em Suat Karataş û Yahya Mehmedoglu re alîkarî dikin, em dixwazin ew her du bibin reîsê belediyê.|nivîskar=Paşa Uzun|sernav=Xalê min Ferîd|url=https://bnk.institutkurde.org/images/pdf/RFTG9DQA76.pdf|weşanxane=Weşanxaneyê Vateyî|sal=2006|rûpel=59}}
# {{înm|Daçekek e bo diyarkirina [[dem]]ê.}}
#: {{nimûne|ku|li sala 2000ê}}
#: {{nimûne|ku|li biharê}}
#: {{nimûne|ku|li êvarê}}
==== Notên bikaranînê ====
{{agahdarî}} Daçeka "li" diyar dike ku tiştek li cihekî ye, daçeka "di ... de" diyar dike ku tiştek di cihekî de ye: "Li Kurdistanê", "li Diyarbekirê", "li meydanê" lê "di kitêbê de", "di qazanê de"(ne "*di Kurdistanê de", ne "di Diyarbekirê de", ne "*di meydanê de" lê herwiha ne "*li kitêbê" û ne "*li qazanê".)
Dema ev daçek serbixwe tê bikaranîn (ango ti [[nomîn]] nadin pey wê), ew dibe '''[[lê]]'''; ''lê kirin'': li wê kirin; li wî kirin; li wan kirin
==== Bi alfabeyên din ====
* {{ku-ar|ل}}
* {{ku-kr|ль}}
==== Formên alternatîv ====
* {{g|ku|l'}}
* {{g|ku|l}}
* {{g|ku|le}} (sùrçî)
* {{g|ku|la}} (nurheqî)
==== Jê ====
{{kol3|ku|cure=Jê
|li alî
|li ber
|li bin
|li der
|li hêlê
|li hindir
|li jêr
|li jor
|li nav
|li rex
|li ser
|li tenişt
}}
==== Etîmolojî ====
Hevreha {{hv|ckb|له|tr=[[le]]}}, hewramî [[ne]], [[hexamenişî]] ''ena'', ji {{mîr|ine-pro|ku|*en}} ([[li]], [[tê de]], [[di ... de]]) û wisa hevreha îngilîzî, almanî, latînî û gelek zimanên din [[in]], fransî û {{hv|es|en}}, {{hv|grc|ἐν|tr=en}}, {{hv|sq|në}}, {{hv|hy|ի|tr=i}}, danmarkî, norwecî û {{hv|sv|i}}, {{hv|lt|į}}, îrlendiya kevn [[i]], weylsî [[yn]]... Bo guherîna N > L bide ber: [[nimêj]] > [[limêj]], [[nivîn]] > [[livîn]].
Heke ne ji {{ji|ine-pro|ku|-}} be, dikare ji [[al]] / [[la]] (rex) be. Dîsa dikare ji {{ji|ar|ku|على}} ('ela: li ser) jî be. Herwiha bide ber {{hv|tr|-la}} / [[-le]] (bi, li gel, bi ... re).
==== Herwiha binêre ====
* {{g|ku|bi}}
* {{g|ku|di}}
* {{g|ku|ji}}
==== Werger ====
{{werger-ser}}
* {{Z|sq}}: {{W-|sq|në}}
* {{Z|de}}: {{W+|de|an}}, {{W+|de|auf}}, {{W+|de|bei}}, {{W+|de|in}}, {{W+|de|zu}}
* {{Z|arc}}:
*: {{Z|syc}}: {{W-|syc|ܒܓܘ|tr=bə-ḡaw}}
*: {{Z|arc}}: {{W-|arc|גַּו|tr=gaw|sc=Hebr}}
* {{Z|be}}: {{W-|be|у}}, {{W-|be|ў}}, {{W-|be|пры}}, {{W-|be|каля́}}
* {{Z|br}}: {{W+|br|e}}
* {{Z|bg}}: {{W+|bg|при}}, {{W+|bg|до}}, {{W+|bg|на}}, {{W+|bg|у}}
* {{Z|my}}: {{W+|my|မှာ}}, {{W+|my|၌}}
* {{Z|cs}}: {{W+|cs|u}}, {{W+|cs|při}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|喺}}, {{W-|yue|在}}
*: {{Z|cmn}}: {{W+|cmn|在}}
* {{Z|da}}: {{W+|da|på}}
* {{Z|ar}}: {{W+|ar|فِي}}
*: {{Z|ary}}: {{W-|ary|في|tr=fi}}, {{W-|ary|فـ|tr=f-}}
*: {{Z|aeb}}: {{W-|aeb|في|tr=fi}}
* {{Z|eo}}: {{W+|eo|ĉe}}
* {{Z|fa}}: {{W+|fa|در|tr=dar}}
* {{Z|fo}}: {{W+|fo|í}}, {{W+|fo|á}}, {{W+|fo|hjá}}, {{W-|fo|til}}
* {{Z|fi}}: {{W+|fi|-lla}}, {{W+|fi|-llä}}, {{W+|fi|-ssa}}, {{W+|fi|-ssä}}, {{W+|fi|luona}}, {{W+|fi|tykönä}}
* {{Z|fr}}: {{W+|fr|à}}, {{W+|fr|en}}
* {{Z|gl}}: {{W+|gl|en}}
* {{Z|got}}: {{W-|got|𐌰𐍄}}
* {{Z|hi}}: {{W+|hi|पर}}, {{W+|hi|में}}, {{W+|hi|से}}
* {{Z|nl}}: {{W+|nl|op}}, {{W+|nl|bij}}
* {{Z|he}}: {{W-|he|אֵצֶל|tr=etsél}}
* {{Z|io}}: {{W+|io|ye}}
* {{Z|en}}: {{W+|en|in}}, {{W+|en|at}}
* {{Z|ia}}: {{W+|ia|a}}, {{W+|ia|in}}
* {{Z|ga}}: {{W-|ga|ag}}
* {{Z|it}}: {{W+|it|a}}, {{W+|it|in}}
* {{Z|is}}: {{W+|is|í}}, {{W+|is|á}}
* {{Z|ja}}: {{W+|ja|で|tr=...de|cuda=...で}}, {{W+|ja|に|tr=...ni|cuda=... に}}
* {{Z|ca}}: {{W+|ca|a}}
* {{Z|ko}}: {{W+|ko|에서|cuda=...에서}}, {{W+|ko|에|cuda=...에}}
* {{Z|ltg}}: {{W-|ltg|pi}}
* Latin: ''[[dative case]]'', {{W+|la|in}}
* {{Z|lv}}: {{W+|lv|pie}}
* Lithuanian: ''[[locative case]]'', {{W+|lt|pas}}
* {{Z|lo}}: {{W-|lo|ທີ່}}
* {{Z|mk}}: {{W-|mk|во}}, {{W-|mk|на}}, {{W-|mk|кај}}, {{W-|mk|при}}
* {{Z|ms}}: {{W+|ms|di}}, {{W+|ms|dekat}}
* {{Z|mnc}}: {{W-|mnc|ᡩᡝ}}
* {{Z|mi}}: {{W+|mi|i}}, {{W-|mi|kei}}, {{W-|mi|hei}}, {{W-|mi|a}}
* {{Z|hu}}: {{W+|hu|-n}}, {{W+|hu|-on}}, {{W+|hu|-en}}, {{W+|hu|-ön}}, {{W+|hu|-nál}}, {{W+|hu|-nél}}
* {{Z|mnw}}: {{W-|mnw|ပ္ဍဲ|tr=pḍay}}
* {{Z|nap}}: {{W-|nap|a}}
* {{Z|nv}}: {{W-|nv|-di}}, {{W-|nv|-gi}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|på}}
*: {{Z|nn}}: {{W+|nn|på}}
* {{Z|pox}}: {{W-|pox|ai̯}}, {{W-|pox|au̯}}
* {{Z|pl}}: {{W+|pl|w}}, {{W+|pl|u}}, {{W+|pl|przy}}
* {{Z|pt}}: {{W+|pt|em}}, {{W+|pt|por}}, {{W+|pt|a}}
* {{Z|rap}}: {{W-|rap|i}}
* {{Z|rom}}: {{W-|rom|kaj}}
* {{Z|ro}}: {{W+|ro|la}}
* {{Z|ru}}: {{W+|ru|в}}, {{W+|ru|на}}, {{W+|ru|у}}, {{W-|ru|во́зле}}
* {{Z|sk}}: {{W+|sk|v}}, {{W-|sk|pri}}, {{W+|sk|na}}
* {{Z|sl}}: {{W+|sl|pri}}, {{W+|sl|na}}, {{W+|sl|ob}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|wu}}, {{W-|dsb|pśi}}
* {{Z|es}}: {{W+|es|en}}
* {{Z|su}}: {{W-|su|dina}}
* {{Z|sw}}: {{W-|sw|karibu na}}
* {{Z|sv}}: {{W+|sv|på}}, {{W+|sv|vid}}
* {{Z|th}}: {{W+|th|ที่}}
* {{Z|te}}: {{W+|te|వద్ద}}
* {{Z|tr}}: {{W+|tr|-da}}, {{W+|tr|-de}}
* {{Z|bo}}: {{W-|bo|ལ}}
* {{Z|tpi}}: {{W-|tpi|long}}
* {{Z|uk}}: {{W+|uk|у}}, {{W+|uk|в}}, {{W+|uk|при}}, {{W-|uk|біля́}}
* {{Z|vi}}: {{W-|vi|ở tại}}, {{W+|vi|lúc}}, {{W+|vi|vào}}, {{W+|vi|ở}}
* {{Z|vo}}: {{W+|vo|pö}}
* {{Z|wa}}: {{W+|wa|a}}
* Welsh: {{sense|in}} {{W+|cy|yn}}, {{W+|cy|mewn}}; {{sense|near}} {{W-|cy|wrth}}, {{W+|cy|ger}}
* {{Z|grc}}: {{W-|grc|ἐν}}, {{W-|grc|εἰς}}
* {{Z|el}}: {{W+|el|στον|n}}, {{W-|el|στη|m}}, {{W+|el|στο|nt}}, {{W-|el|στους|n-p}}, {{W+|el|στις|m-p}}, {{W+|el|στα|nt-p}}
* {{Z|zea}}: {{W-|zea|bie}}, {{W-|zea|in}}, {{W-|zea|nessens}}
{{werger-bin}}
:{{werger çavkanî|en|at|beş=Ev qismê Wergerê|oldid=75667425}}
== {{ziman|eo}} ==
=== Bilêvkirin ===
{{eo-IPA}}
* {{deng|eo|LL-Q143 (epo)-Robin van der Vliet-li.wav|Deng|dever=Wijchen, Holenda}}
* {{deng|eo|LL-Q143 (epo)-NMaia-li.wav|Deng|dever=Brezîl}}
* {{deng|eo|LL-Q143 (epo)-Lepticed7-li.wav|Deng|dever=Toulouse, Fransa}}
* {{deng|eo|LL-Q143 (epo)-Poslovitch-li.wav|Deng|dever=Vosges, Fransa}}
=== Cînav ===
{{eo-tewîn-cînav}}
{{cînav|eo}}
# (mêr) [[ew]], [[wî]]
#: {{mk|eo|'''Li''' amas vin.|'''Ew''' ji te hez dike.|rêz=1}}
#: {{mk|eo|'''Li''' skribis.|'''Wî''' nivîsand.|rêz=1}}
==== Jê ====
* {{g|eo|ili}}
==== Etîmolojî ====
Deyn ji {{ji|es|eo|le}}, {{ji|it|eo|lui}}, {{ji|fr|eo|lui}}.
==== Herwiha binêre ====
{{eo-cînav}}
a3plymjda8jgneoayqm9wzi0kol5fkn
seat
0
4788
6334131
6317156
2026-08-16T17:14:10Z
Wikihez
7344
/* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334131
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{guharto|ku|saet}}
#* {{Jêgirtin/Mela Hisênê Bateyî/Mewlûda Kurdî|jêgirtin=Têkveda heta ku ew qenc bû xemîr</br>Bû wekî durra sipî wê '''se'atê'''</br>Xewte da der çûyî barê Cennetê}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{ku-tîp|sît}}
* {{deng|en|en-us-seat.ogg|dever=DYA}}
* {{deng|en|LL-Q1860 (eng)-Nattes à chat-seat.wav|Deng|dever=}}
* {{deng|en|LL-Q1860 (eng)-Wodencafe-seat.wav|Deng|dever=Teksas, DYA}}
* {{deng|en|LL-Q1860 (eng)-Grendelkhan-seat.wav|Deng|dever=Connecticut, DYA}}
=== Navdêr ===
{{navdêr|en}}
# [[kursî]], [[rûniştek]], [[meqeet]]
#* {{bêmînak|en}}
# [[meqeet]]
#: {{mk|en|There is a new '''seat''' for linguistics at the university}}
# [[rûniştin]]
#: {{mk|en|Have a seat, please.|Kerem kin, rûnin.}}
41qfq60vph2tuei32g5hvfpe49mzxz2
xiyar
0
5515
6334149
6328404
2026-08-16T17:25:35Z
Wikihez
7344
/* Name */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334149
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{stûn|
* {{mj|lêker}} [[xiyar kirin]]
* {{mj|navdêr}} [[xiyarkirin]]
* {{mj|rengdêr}} [[xiyarkirî]]
* [[xiyarî]]
* [[xiyark]]
* [[xiyarok]]
* [[xiyarmast]]
* [[tirşîya xiyaran]]
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Bide ber ====
* [[tirozî]]
==== Pîrepend ====
* [[eger xiyar e, bi tûtkê diyar e]]
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Herwiha ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
r00na1jgw7u8kb98d01iw4y3ly8p2w1
6334150
6334149
2026-08-16T17:25:51Z
Wikihez
7344
/* Jê */
6334150
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{kol3|ku|cure=Jê
|xiyar kirin<q:lêker>
|xiyarkirin<q:navdêr>
|xiyarkirî<q:rengdêr>
|xiyarî
|xiyark
|xiyarok
|xiyarmast
|tirşîya xiyaran
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Bide ber ====
* [[tirozî]]
==== Pîrepend ====
* [[eger xiyar e, bi tûtkê diyar e]]
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Herwiha ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
pxlmanqtu4fsltg913i3lgyfvaqqmof
6334153
6334150
2026-08-16T17:41:06Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334153
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{kol3|ku|cure=Jê
|xiyar kirin<q:lêker>
|xiyarkirin<q:navdêr>
|xiyarkirî<q:rengdêr>
|xiyarî
|xiyark
|xiyarok
|xiyarmast
|tirşîya xiyaran
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Bide ber ====
* [[tirozî]]
==== Pîrepend ====
* [[eger xiyar e, bi tûtkê diyar e]]
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Formên alternatîv ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
dwiy6itn753wdf2aweqjmsos3klgbvm
6334154
6334153
2026-08-16T17:42:33Z
Wikihez
7344
6334154
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{kol3|ku|cure=Jê
|xiyar kirin<q:lêker>
|xiyarkirin<q:navdêr>
|xiyarkirî<q:rengdêr>
|xiyarî
|xiyark
|xiyarok
|xiyarmast
|tirşîya xiyaran
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Binêre herwiha ====
{{kol3|ku
|eger xiyar e, bi tûtkê diyar e<q:pîrepend>
|tirozî
}}
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Formên alternatîv ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
2y33rhi2qr8j6eixvhu8c3xqaccha3i
6334155
6334154
2026-08-16T17:42:44Z
Wikihez
7344
/* Binêre herwiha */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334155
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{kol3|ku|cure=Jê
|xiyar kirin<q:lêker>
|xiyarkirin<q:navdêr>
|xiyarkirî<q:rengdêr>
|xiyarî
|xiyark
|xiyarok
|xiyarmast
|tirşîya xiyaran
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Binêre herwiha ====
{{kol3|ku
|eger xiyar e, bi tûtkê diyar e<qq:pîrepend>
|tirozî
}}
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Formên alternatîv ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
k8u8juc5wt44a17h0m4cym429y06m5x
6334187
6334155
2026-08-16T18:41:20Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334187
wikitext
text/x-wiki
{{bnr-h|xîyar}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|xi|yar}}
=== Navdêr ===
[[Wêne:Cucumber and cross section.jpg|thumb|320px|'''Xiyarek''' û [[teqal]]ek xiyarî.]]
[[Wêne:Cucumis sativus0.jpg|thumb|Kulîlkên '''xiyarê'''.]]
{{navdêr|ku|z=m}}
# [[sebze|Sebzeyek]] (bi rastî fêkî) [[dirêjok]] û [[tîvil]]kesk e {{î|bi taybetî [[zelete]] jê tê çêkirin}}.
#: {{hevmane|ku|arû}}
#* {{jêder-kitêb|ku|nivîskar=Ehmedê Xanî|amadekar=Z. Kaya|sernav=Nûbar|sala weşandinê=1986|sal=1683|r=18|jêgirtin=Rumman hınar{{sic}} e, culnar gılyaz, û tiyn hejir e.<br>Qıssa '''xiyar''' e, dewfed pivaz e, sewm ı sir e.|isbn=91 7672 017 9|cih=Stokholm}}
#* {{jêder|ku|jêgirtin=Wan ji xwe re hin '''xiyar''', firingî û penîr kirîn, çûne nav bexçeyekî, li bin siya darekê rûniştin û bi kêf û henek xwarina xwe xwarin.|{{Jêgirtin/Firat Cewerî/Girtî}}}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خیار}}
==== Formên alternatîv ====
* [[xeyar]]
==== Navê zanistî ====
* ''[[Cucumis sativus]]''
==== Jê ====
{{kol3|ku|cure=Jê
|xiyar kirin<q:lêker>
|xiyarkirin<q:navdêr>
|xiyarkirî<q:rengdêr>
|xiyarî
|xiyark
|xiyarok
|xiyarmast
|tirşîya xiyaran
}}
==== Etîmolojî ====
Ji {{ji|ira}}, hevreha {{hv|ckb|خیار|tr=xiyar}}, {{hv|fa|خیار|tr=xiyar}}, {{hv|pal|xiyar|sc=Latn}}.
==== Herwiha binêre ====
{{kol3|ku
|eger xiyar e, bi tûtkê diyar e<qq:pîrepend>
|tirozî
}}
==== Werger ====
{{werger-ser|riwek}}
* {{Z|af}}: {{W+|af|komkommer}}
* {{Z|sq}}: {{W+|sq|trangull|n}}, {{W+|sq|kastravec|n}}
* {{Z|de}}: {{W+|de|Gurke|m}}
* {{Z|alt}}: {{W-|alt|огурчын}}
* {{Z|am}}: {{W-|am|ኪያር|n}}
* {{Z|as}}: {{W-|as|তিয়ঁহ}}
* {{Z|ast}}: {{W+|ast|pepinu|n}}
* {{Z|az}}: {{W+|az|xiyar}}
* {{Z|ba}}: {{W-|ba|ҡыяр}}
* {{Z|be}}: {{W+|be|агуро́к|n}}
* {{Z|bn}}: {{W+|bn|শসা}}
* {{Z|kxd}}: {{W-|kxd|timun}}
* {{Z|bg}}: {{W+|bg|кра́ставица|m}}
* {{Z|my}}: {{W+|my|သခွား}}
* {{Z|ce}}: {{W-|ce|наьрс}}
* {{Z|cs}}: {{W+|cs|okurka|m}}
* {{Z|cv}}: {{W-|cv|хӑяр}}
* {{Z|da}}: {{W+|da|agurk}}
* {{Z|id}}: {{W+|id|mentimun}}, {{W+|id|timun}}, {{W+|id|ketimun}}
* {{Z|arz}}: {{W-|arz|خيارة|m}}, {{W-|arz|خيار}}
* {{Z|ar}}: {{W+|ar|خِيَار|n}}
* {{Z|hy}}: {{W+|hy|վարունգ}}
* {{Z|eo}}: {{W-|eo|kukumo}}
* {{Z|et}}: {{W+|et|kurk}}
* {{Z|ee}}: {{W-|ee|adzamtre}}
* {{Z|fa}}: {{W+|fa|خیار}}
* {{Z|fo}}: {{W-|fo|agurka|m}}
* {{Z|fi}}: {{W+|fi|kurkku}}
* {{Z|fr}}: {{W+|fr|concombre|n}}
* {{Z|fur}}: {{W-|fur|cudumar|n}}
* {{Z|gd}}: {{W-|gd|cularan|n}}
* {{Z|gl}}: {{W+|gl|cogombro|n}}
* {{Z|ka}}: {{W+|ka|კიტრი}}
* {{Z|haw}}: {{W-|haw|kaʻukama}}
* {{Z|hi}}: {{W+|hi|ककड़ी|m}}, {{W+|hi|खीरा|n}}
* {{Z|nl}}: {{W+|nl|komkommer|n}}
* {{Z|he}}: {{W+|he|מְלָפְפוֹן|n}}
* {{Z|io}}: {{W+|io|kukombro}}
* {{Z|ang}}: {{W-|ang|eorþæppel|n}}
* {{Z|ga}}: {{W-|ga|cúcamar}}
* {{Z|it}}: {{W+|it|cetriolo|n}}
* {{Z|is}}: {{W+|is|gúrka|m}}, {{W+|is|agúrka|m}}
* {{Z|ja}}: {{W+|ja|胡瓜}}, {{W+|ja|キュウリ}}
* {{Z|kl}}: {{W-|kl|agurki}}
* {{Z|yue}}: {{W|yue|青瓜}}
* {{Z|ca}}: {{W+|ca|cogombre|n}}
* {{Z|ky}}: {{W+|ky|бадыраң}}
* {{Z|si}}: {{W+|si|පිපිඤ්ඤා}}
* {{Z|ko}}: {{W+|ko|오이}}
* {{Z|kw}}: {{W-|kw|kukomber|n}}
* {{Z|kum}}: {{W-|kum|хыяр}}
* {{Z|la}}: {{W+|la|cucumis|n}}
* {{Z|lv}}: {{W+|lv|gurķis|n}}
* {{Z|lo}}: {{W+|lo|ໝາກແຕງ}}
* {{Z|lt}}: {{W-|lt|agurkas|n}}
* {{Z|mk}}: {{W-|mk|краставица|m}}
* {{Z|ms}}: {{W+|ms|timun}}
* {{Z|mt}}: {{W-|mt|ħjara}}
* {{Z|cmn}}: {{W+|cmn|黃瓜}}, {{W+|cmn|黄瓜}}, {{W+|cmn|青瓜}}
* {{Z|hu}}: {{W+|hu|uborka}}
* {{Z|nan}}: {{W+|nan|刺瓜}}, {{W+|nan|黃瓜}}, {{W-|nan|黄瓜}}
* {{Z|mn}}: {{W-|mn|өргөст хэмх}}, {{W-|mn|огурцы}}
* {{Z|nv}}: {{W-|nv|taʼneeskʼání áłtsʼóózígíí}}
* {{Z|nrf}}: {{W-|nrf|cocombre|m}}
* {{Z|nb}}: {{W+|nb|agurk|n}}
* {{Z|nn}}: {{W-|nn|agurk|n}}
* {{Z|oc}}: {{W+|oc|cogombre|n}}
* {{Z|ota}}: {{W-|ota|خیار}}
* {{Z|uz}}: {{W+|uz|bodring}}
* {{Z|pl}}: {{W+|pl|ogórek|n}}
* {{Z|pt}}: {{W+|pt|pepino|n}}
* {{Z|kk}}: {{W-|kk|қияр}}, {{W-|kk|бәдірен}}
* {{Z|rm}}: {{W-|rm|cucumera|m}}
* {{Z|ro}}: {{W+|ro|castravete|n}}
* {{Z|ru}}: {{W+|ru|огуре́ц|n}}
* {{Z|sh}}: {{W-|sh|кра̏ставац|n}}, {{W+|sh|krȁstavac|n}}
* {{Z|sk}}: {{W+|sk|uhorka|m}}
* {{Z|sl}}: {{W+|sl|kumara|m}}
* {{Z|dsb}}: {{W-|dsb|górka|m}}
* {{Z|es}}: {{W+|es|pepino|n}}
* {{Z|sw}}: {{W-|sw|tango}}
* {{Z|sv}}: {{W+|sv|gurka}}
* {{Z|tg}}: {{W+|tg|бодиринг}}
* {{Z|tl}}: {{W-|tl|pipino}}
* {{Z|ta}}: {{W+|ta|வெள்ளரிக்காய்}}
* {{Z|th}}: {{W+|th|แตงกวา}}
* {{Z|te}}: {{W+|te|దోసకాయ}}
* {{Z|tt}}: {{W+|tt|кыяр}}
* {{Z|tr}}: {{W+|tr|hıyar}}, {{W+|tr|salatalık}}
* {{Z|tk}}: {{W-|tk|hyýar}}
* {{Z|ur}}: {{W-|ur|ککڑی|m}}, {{W-|ur|کھیرا|n}}
* {{Z|uk}}: {{W+|uk|огіро́к}}
* {{Z|vec}}: {{W-|vec|zetriol}}
* {{Z|vi}}: {{W-|vi|dưa chuột}}, {{W+|vi|dưa leo}}
* {{Z|cy}}: {{W+|cy|ciwcymbr|n}}, {{W-|cy|ciwcymer|n}}, {{W+|cy|cucumer|n}}, {{W-|cy|chwerwddwr|n}}
* {{Z|kjh}}: {{W-|kjh|ӱгурсӱ}}
* {{Z|km}}: {{W+|km|ត្រសក់}}, {{W+|km|តាសក់}}
* {{Z|sah}}: {{W-|sah|огурсу}}
* {{Z|yi}}: {{W-|yi|אוגערקע|m}}
* {{Z|grc}}: {{W-|grc|σίκυος|n}}, {{W-|grc|σικυός|n}}, {{W-|grc|σίκυς|m}}, {{W-|grc|ἀγγούριον}}
* {{Z|el}}: {{W+|el|αγγούρι}}
* {{Z|za}}: {{W|za|lwgbieng}}
{{werger-bin}}
{{mijar|ku|sebze}}
== {{ziman|zza}} ==
=== Bilêvkirin ===
* {{IPA|zza|[xiːˈɑɾ]}}
* {{kîte|zza|xi|yar}}
* {{deng|zza|LL-Q10199 (zza)-Mirzali-xiyar.wav|Deng (Pilemor)}}
=== Navdêr ===
{{navdêr|zza|z=n}}
# {{f|zza|sebze}} {{g|ku|xiyar}}
==== Formên alternatîv ====
* {{g|zza|xîyar}}
== {{ziman|az}} ==
=== Navdêr ===
{{navdêr|az}}
# {{f|az|sebze}} {{g|ku|xiyar}}
#* {{bêmînak|az}}
l3a10lms4togu7tszwwz54yzi1hjyst
sewik
0
9612
6334208
6325064
2026-08-16T20:09:57Z
Zerwanda
12183
/* Etîmolojî */
6334208
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:Beqsem.jpg|thumb|Sewikên bi şêwê kurdên rojavayî (2)]]
{{navdêr|ku|z=m}}
# [[nan|Nanê]] [[pan]] û [[girover]] yên di [[tenûr]]ê de tê [[patin]].
#* {{bênimûne|ku}}
# [[kade]], [[beqsem]]
#* {{bênimûne|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Formên alternatîv ====
* [[sewk]]
==== Têkildar ====
* [[tenûr]]
* [[tîrok]]
==== Etîmolojî ====
Hevreh bi {{hv|ckb|سەپک}}, {{q|ckb|سەپلە}}, ji {{q|ku|se}} çumkî elsen ji boyî kûçikan dihate çêkirin.
==== Bide ber ====
* [[semon]]
* [[sêl|nanê sêlê]]
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|الشكلمة}}, {{W-|ar|الرغیف|guman=erê}}
* {{Z|en}}: {{W+|en|manchet}}, {{W+|en|disk}}, {{W+|en|loaf}}, (2) {{W+|en|biscuit}}, {{W+|en|pastry}}
{{werger-bin}}
k3qwifm14uchfnsjtlmts7ftuz4a9xz
6334209
6334208
2026-08-16T20:10:04Z
Zerwanda
12183
6334209
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:Beqsem.jpg|thumb|Sewikên bi şêwê kurdên rojavayî (2)]]
{{navdêr|ku|z=m}}
# [[nan|Nanê]] [[pan]] û [[girover]] yên di [[tenûr]]ê de tê [[patin]].
#* {{bênimûne|ku}}
# [[kade]], [[beqsem]]
#* {{bênimûne|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Formên alternatîv ====
* [[sewk]]
==== Têkildar ====
* [[tenûr]]
* [[tîrok]]
==== Etîmolojî ====
Hevreh bi {{hv|ckb|سەپک}}, {{q|ckb|سەپلە}}, ji {{q|ku|se}} çumkî eslen ji boyî kûçikan dihate çêkirin.
==== Bide ber ====
* [[semon]]
* [[sêl|nanê sêlê]]
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|الشكلمة}}, {{W-|ar|الرغیف|guman=erê}}
* {{Z|en}}: {{W+|en|manchet}}, {{W+|en|disk}}, {{W+|en|loaf}}, (2) {{W+|en|biscuit}}, {{W+|en|pastry}}
{{werger-bin}}
kocahpw25ojr4g65sbyyzairxno7vx7
kuçk
0
16938
6334182
6244617
2026-08-16T18:32:22Z
Zerwanda
12183
6334182
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{wp}}
{{navdêr|ku|z=n}}
# [[cih|Cih]]ê [[agir]] tê de têt [[hilkirin]]. (bi taybetî li [[derve]])
#: {{hevmaneyên peyvê|ku|argûn}}
#* {{jêder|ku|jêgirtin=Gaveke bi şûnde li raxeke '''kuçka''' xwe ya agir rûniştibû|{{Jêgirtin/Robert Louis Stevenson/Meseleya Seyr a Dr. Jekyll û Mr. Hyde|r=15}}}}
==== Bi alfabeyên din ====
* {{ku-ar|کوچک}}
==== Formên alternatîv ====
* {{g|ku|kuçik}}
==== Bide ber ====
* {{g|ku|kaçik}}
* {{g|ku|keçik}}
* {{g|ku|koçik}}
* {{g|ku|kûçik}}
==== Etîmolojî ====
Ji {{paş|ku|kuç|-k}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W|ar|مستوقد}}, {{W|ar|الأتون|guman=erê}}, {{W|ar|الإراث|guman=erê}}
* {{Z|en}}: {{W|en|fireplace}}, {{W|en|chimney}} {{W|en|turnspit|guman=erê}}
* {{Z|it}}: {{W+|it|focolare}}
* {{Z|tr}}: {{W+|tr|ocak}}, {{W+|tr|ocaklık}}
{{werger-bin}}
ap6rv74joechhyfrop6qy504ewxf7yb
6334183
6334182
2026-08-16T18:41:08Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334183
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{wp}}
{{navdêr|ku|z=n}}
# [[cih|Cih]]ê [[agir]] tê de têt [[hilkirin]]. (bi taybetî li [[derve]])
#: {{hevmaneyên peyvê|ku|argûn}}
#* {{jêder|ku|jêgirtin=Gaveke bi şûnde li raxeke '''kuçka''' xwe ya agir rûniştibû|{{Jêgirtin/Robert Louis Stevenson/Meseleya Seyr a Dr. Jekyll û Mr. Hyde|r=15}}}}
==== Bi alfabeyên din ====
* {{ku-ar|کوچک}}
==== Formên alternatîv ====
* {{g|ku|kuçik}}
==== Etîmolojî ====
Ji {{paş|ku|kuç|-k}}.
==== Bide ber ====
* {{g|ku|kaçik}}
* {{g|ku|keçik}}
* {{g|ku|koçik}}
* {{g|ku|kûçik}}
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W|ar|مستوقد}}, {{W|ar|الأتون|guman=erê}}, {{W|ar|الإراث|guman=erê}}
* {{Z|en}}: {{W|en|fireplace}}, {{W|en|chimney}} {{W|en|turnspit|guman=erê}}
* {{Z|it}}: {{W+|it|focolare}}
* {{Z|tr}}: {{W+|tr|ocak}}, {{W+|tr|ocaklık}}
{{werger-bin}}
me4miyknu8qim4e7goq3darv7xj1bcu
6334210
6334183
2026-08-16T20:12:06Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir, --Valahiyên nehewce.)
6334210
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{wp}}
{{navdêr|ku|z=n}}
# [[cih|Cihê]] [[agir]] tê de têt [[hilkirin]]. (bi taybetî li [[derve]])
#: {{hevmaneyên peyvê|ku|argûn}}
#* {{jêder|ku|jêgirtin=Gaveke bi şûnde li raxeke '''kuçka''' xwe ya agir rûniştibû|{{Jêgirtin/Robert Louis Stevenson/Meseleya Seyr a Dr. Jekyll û Mr. Hyde|r=15}}}}
==== Bi alfabeyên din ====
* {{ku-ar|کوچک}}
==== Formên alternatîv ====
* {{g|ku|kuçik}}
==== Etîmolojî ====
Ji {{paş|ku|kuç|-k}}.
==== Bide ber ====
* {{g|ku|kaçik}}
* {{g|ku|keçik}}
* {{g|ku|koçik}}
* {{g|ku|kûçik}}
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W|ar|مستوقد}}, {{W|ar|الأتون|guman=erê}}, {{W|ar|الإراث|guman=erê}}
* {{Z|en}}: {{W|en|fireplace}}, {{W|en|chimney}} {{W|en|turnspit|guman=erê}}
* {{Z|it}}: {{W+|it|focolare}}
* {{Z|tr}}: {{W+|tr|ocak}}, {{W+|tr|ocaklık}}
{{werger-bin}}
3p3mkw5ex67xbdv22qj7qg7mkkkpybv
cîran
0
20084
6334127
6304441
2026-08-16T16:49:09Z
~2026-42026-75
15188
/* Etîmolojî */
6334127
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{IPA|ku|/d͡ʒiːˈɾɑːn/}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-cîran.wav|dever=Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|c=mn}}
# [[kes|Kes]]ên [[mal]]ên wan [[nêz]]î [[hev]]; kesên li [[nêzîk]]î [[hev]] [[jîn|dijîn]].
#: {{hevmane|ku|dirawse|cînar|hevsa|hemsa|hevsî|hemsî}}
#: {{mk|ku|Em û ew '''cîran''' in.|Mala me û ya wan nêzî hev e.|birêz=1}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Mawer ke terh û bişkoyan</br>'''Cîranê''' berg û lû'lûyan</br>Ew 'er'er û selwa ciwan}}
#* {{jêder|ku|jêgirtin=Di nava ekradan de teklîf tunînin. Wekû tiştek lazim bibitin, û li mala '''cîranan''' ew tişt peyda bibitin, bilanî xweyî malê jî hazir nebitin, ew mirov bi diçitin û ew tiştê lazim buyî tînitin. |{{jêgirtin/Mele Mehmûdê Bayezîdî/Adat û Rusûmatnameê Ekradiye|r=125}}}}
# [[welat|Welat]]ên [[hevsinor]], [[dewlet]]ên ku [[tixûb]] bi hev re heye.
#: {{hevmane|ku|dirawse|cînar}}
#: {{nm|ku|Rûsya bi axa xwe welatê cîhanê yê herî mezin e û bi çardeh dewletan re '''cîran''' e.}}
#: {{mk|ku|Ereb, faris, ermenî û aşûrî '''cîranên''' me ne û yên herî kevin in, lê mixabin di bareya wêjeya wan de tiştekî nizanim ko bikaribim ya me û yên wan bidim ber hev.}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Bi alfabeyên din ====
* {{ku-ar|جيران}}
==== Formên alternatîv ====
* {{g|ku|cînar}}
==== Jê ====
{{stûn|
* {{mj|lêker}} {{g|ku|cîran bûn}}
* {{mj|lêker}} {{g|ku|cîran kirin}}
* {{mj|navdêr}} {{g|ku|cîranbûn}}
* {{mj|navdêr}} {{g|ku|cîrankirin}}
* {{mj|rengdêr}} {{g|ku|cîranbûyî}}
* {{mj|rengdêr}} {{g|ku|cîrankirî}}
* {{g|ku|cîranane}}
* {{g|ku|cîranî}}
* {{g|ku|cîranîtî}}
* {{g|ku|cîrantî}}
}}
==== Etîmolojî ====
Ji {{ji|ar|ku|جيران|tr=cîran}}, firejimar a {{g|ar|جار|tr=car|w=cîran, hevsî}}, ji lêkera {{g|ar|جار|tr=care|w=nêzîk bûn}}. Bi [[etîmolojiya gelêrî]] gelek caran têkil bi {{q|ku|cî}} tê kirin. Li pitirîna deveran ewa xwecih {{q|ku|hevsî}} ji cih kiriye, bide ber {{hv|ckb|دراوسێ|tr=[[dirawsê]]}} û {{hv|zza|embiriyane}}.
# Gelo bi kurmancî dibe HIMBER /HIMBERVAN ?
==== Werger ====
{{werger-ser}}
* {{Z|ab}}: {{W-|ab|агәыла}}
* {{Z|af}}: {{W-|af|buurman|n}}, {{W-|af|buurvrou|n}}
* {{Z|sq}}: {{W+|sq|fqinj|n}}, {{W-|sq|fqije|m}}, {{W-|sq|gjiton|n}}, {{W+|sq|komshi|n}}
* {{Z|goh}}: {{W-|goh|nāhgibūr}}
* {{Z|pdc}}: {{W-|pdc|Nochber}}
* {{Z|de}}: {{W+|de|Nachbar|n}}, {{W+|de|Nachbarin|m}}
* {{Z|ryn}}: {{W-|ryn|隣|tr=と゚なり, thonari}}
* {{Z|ams}}: {{W-|ams|隣|tr=とぅない, tunai}}
* {{Z|am}}: {{W-|am|ጎረቤት}}
* {{Z|arc}}:
*: Îbranî: {{W-|arc|שבבא|n|tr=šbābā’}}, {{W-|arc|שבבתא|m|tr=šbābtā’}}
*: Siryanî: {{W-|arc|ܫܒܒܐ|n|tr=šbābā’|}}, {{W-|arc|ܫܒܒܬܐ|m|tr=šbābtā’|}}
* {{Z|rup}}: {{W-|rup|vitsin|n}}
* {{Z|ast}}: {{W+|ast|vecín|n}}, {{W+|ast|vecina|m}}
* {{Z|az}}: {{W+|az|qonşu}}
* {{Z|eu}}: {{W-|eu|aldameneko}}, {{W-|eu|auzokide}}
* {{Z|ba}}: {{W-|ba|күрше}}
* {{Z|bar}}: {{W-|bar|Nochbar}}, {{W-|bar|Nåchbår}}
* {{Z|be}}: {{W-|be|сусе́д|n}}, {{W-|be|сусе́дка|m}}
* {{Z|bal}}: {{W-|bal|ہمسایگ|tr=hamsáyag}}, {{W-|bal|ہمساہگ|tr=hamsáhag, hamsáhig}}
* {{Z|bn}}: {{W-|bn|প্রতিবেশী}}
* {{Z|br}}: {{W+|br|amezeg|n}}, {{W+|br|amezegez|m}}
* {{Z|bg}}: {{W+|bg|съсе́д|n}}, {{W+|bg|съсе́дка|m}}, {{W+|bg|комши́я|n}}, {{W-|bg|комши́йка|m}}
* {{Z|my}}: {{W+|my|အိမ်နီးချင်း}}
* {{Z|bua}}: {{W-|bua|хүршэ}}
* {{Z|ce}}: {{W-|ce|лулахо}}
* {{Z|cs}}: {{W+|cs|soused|n}}, {{W+|cs|sousedka|m}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|鄰居|tr=leon4 geoi1}}
*: {{Z|dng}}: {{W-|dng|линҗү}}, {{W-|dng|гибир}}
*: {{Z|cmn}}: {{W-|cmn|鄰居|tr=línjū}}, {{W-|cmn|比鄰|tr=bǐlín, bìlín}}
*: {{Z|nan}}: {{W+|nan|厝邊|tr=chhù-piⁿ}}
* {{Z|dlm}}: {{W-|dlm|vičain|n}}
* {{Z|da}}: {{W+|da|nabo|g}}
* {{Z|ovd}}: {{W-|ovd|granne|n}}
* {{Z|id}}: {{W+|id|tetangga}}
* {{Z|ar}}: {{W+|ar|جَار|n}}, {{W+|ar|جَارة|m}}
*: {{Z|arz}}: {{W-|arz|جار|n|tr=gār}}, {{W-|arz|جارة|m|tr=gāra}}
*: {{Z|acw}}: {{W-|acw|جار|n|tr=jār}}, {{W-|acw|جارة|m|tr=jāra}}
*: {{Z|ary}}: {{W-|ary|جار|n|tr=jār}}, {{W-|ary|جارة|m|tr=jāra}}
* {{Z|hy}}: {{W-|hy|հարեւան}}, {{W+|hy|դրացի}}
* {{Z|eo}}: {{W+|eo|najbaro}}
* {{Z|et}}: {{W+|et|naaber}}
* {{Z|fa}}: {{W+|fa|همسایه|tr=hamsâye}}
* {{Z|fo}}: {{W-|fo|granni|n}}, {{W-|fo|nábúgvi|n}}
* {{Z|fi}}: {{W+|fi|naapuri}}
* {{Z|fr}}: {{W+|fr|voisin|n}}, {{W+|fr|voisine|m}}
* {{Z|fur}}: {{W-|fur|vicin|n}}, {{W-|fur|vicìn|n}}, {{W-|fur|prossim|n}}
* {{Z|frr}}: {{W-|frr|näiber}}
* {{Z|gd}}: {{W-|gd|nàbaidh|n}}
* {{Z|gl}}: {{W+|gl|veciño}}
* {{Z|got}}: {{W-|got|𐌱𐌹𐍃𐌹𐍄𐌰𐌽𐌳𐍃|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐌰|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐍉|m}}, {{W-|got|𐌽𐌴𐍈𐌿𐌽𐌳𐌾𐌰|n}}
* {{Z|ka}}: {{W+|ka|მეზობელი}}
* {{Z|mba}}: {{W-|mba|silingan}}
* {{Z|hi}}: {{W+|hi|पड़ोसी|n}}, {{W-|hi|पड़ोसिन|m}}, {{W+|hi|हमसाया|n}}
* {{Z|nl}}: {{W+|nl|buur|n}}, {{W+|nl|buurman|n}}, {{W+|nl|buurvrouw|m}}, {{W+|nl|buren|p}}, {{W+|nl|nabuur|n}}
* {{Z|he}}: {{W+|he|שָׁכֵן|n|tr=shakhén}}, {{W-|he|שְׁכֵנָה|m}}
* {{Z|io}}: {{W+|io|vicino}}, {{W-|io|vicinulo}}, {{W-|io|vicinino}}
* {{Z|ang}}: {{W-|ang|nīehsta|n}}, {{W-|ang|nēahġebūr|n}}
* {{Z|enm}}: {{W-|enm|neyghebour}}
* {{Z|en}}: {{W+|en|neighbour}}, {{W+|en|neighbor}}
* {{Z|ia}}: {{W-|ia|vicino}}
* {{Z|ga}}: {{W-|ga|comharsa|m}}
* {{Z|ist}}: {{W-|ist|viseîn|n}}
* {{Z|it}}: {{W+|it|vicino|n}}, {{W+|it|vicina|m}}
* {{Z|is}}: {{W+|is|nágranni|n}}, {{W+|is|nábúi|n}}
* {{Z|ja}}: {{W+|ja|隣人|tr=りんじん, rinjin}}, {{W-|ja|[[隣]]の[[人]]|tr=となりのひと, tonari no hito}}, {{W-|ja|[[近所]]の[[人]]|tr=きんじょのひと, kinjo no hito}}, {{W-|ja|[[隣家]]の[[人]]|tr=りんかのひと, rinka no hito}}
* {{Z|jje}}: {{W-|jje|이웃}}, {{W-|jje|윳}}
* {{Z|kl}}: {{W-|kl|sanileq}}, {{W-|kl|eqqaamiu}}
* {{Z|pam}}: {{W-|pam|siping-bale}}
* {{Z|ca}}: {{W+|ca|veí|n}}, {{W+|ca|veïna|m}}
* {{Z|kzg}}: {{W-|kzg|隣|tr=と゚ぅない, thunai}}
* {{Z|ky}}: {{W-|ky|коңшу}}, {{W-|ky|кошуна}}, {{W-|ky|көршу}}
* {{Z|ko}}: {{W-|ko|[[이웃]] [[사람]]}}, {{W+|ko|이웃}}
* {{Z|xug}}: {{W-|xug|隣|tr=とぅない, tunai}}
* {{Z|ltg}}: {{W-|ltg|kaimiņs|n}}, {{W-|ltg|sābrys|n}}
* {{Z|la}}: {{W-|la|adcola|n|m}}, {{W-|la|accola|n|m}}, {{W-|la|vīcīnus|n}}, {{W-|la|vīcīna|m}}
* {{Z|lv}}: {{W+|lv|kaimiņš|n}}, {{W-|lv|kaimiņiene|m}}
* {{Z|lo}}: {{W-|lo|ເພື່ອນບ້ານ}}
* {{Z|lt}}: {{W+|lt|kaimynas|n}}, {{W-|lt|kaimynė|m}}
* {{Z|lmo}}: {{W-|lmo|visin}}, {{W-|lmo|vesin}}
* {{Z|lb}}: {{W-|lb|Noper|n}}, {{W-|lb|Nopesch|m}}
* {{Z|mk}}: {{W+|mk|сосед|n}}, {{W-|mk|сосетка|m}}, {{W+|mk|комшија|n}}, {{W-|mk|комшика|m}}
* {{Z|ms}}: {{W+|ms|jiran}}, {{W-|ms|tetangga}}
* {{Z|mt}}: {{W-|mt|ġar|n}}
* {{Z|gv}}: {{W-|gv|naboo}}
* {{Z|mi}}: {{W-|mi|tangata-noho-tata}}, {{W-|mi|kiritata}}
* {{Z|hu}}: {{W+|hu|szomszéd}}
* {{Z|mvi}}: {{W-|mvi|隣|tr=とぅなㇲ゙, tunaz}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|хөрш}}
*: Mongolî: {{W-|mn|ᠬᠥᠷᠰᠢ}}
* {{Z|wlc}}: {{W-|wlc|djirani}}
* {{Z|nds}}: {{W-|nds|Nahwer|n}}
* {{Z|non}}: {{W-|non|granni|n}}, {{W-|non|nábúi}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|nabo|n}}
*: {{Z|nn}}: {{W-|nn|nabo|n}}
* {{Z|okn}}: {{W-|okn|隣|tr=とぅない, tunai}}
* {{Z|ryu}}: {{W-|ryu|隣|tr=とぅない, tunai}}
* {{Z|oc}}: {{W+|oc|vesin|n}}
* {{Z|om}}: {{W-|om|ollaa}}
* {{Z|ota}}: {{W-|ota|قونشو|tr=konşu, komşu}}
* {{Z|ug}}: {{W-|ug|قوشنا}}, {{W-|ug|خوشنا}}
* {{Z|uz}}: {{W+|uz|hamsoya}}, {{W+|uz|qoʻshni}}
* {{Z|pap}}: {{W-|pap|bisiña}}
* {{Z|ps}}: {{W-|ps|همسايه|m|tr=hamsāya}}, {{W-|ps|ګاونډی|n|tr=gāwanḍay}}
* {{Z|pms}}: {{W-|pms|vsin|n}}
* {{Z|pl}}: {{W+|pl|sąsiad|n}}, {{W+|pl|sąsiadka|m}}
* {{Z|pt}}: {{W+|pt|vizinho|n}}, {{W+|pt|vizinha|m}}
* Punjabi:
*: Gurmuxî: {{W-|pa|ਗੁਆਂਢੀ|n}}, {{W-|pa|ਗਵਾਂਢੀ|n}}, {{W-|pa|ਗੁਆਂਢਣ|m}}
*: Şahmuxî: {{W-|pa|گَوانڈھی|n}}, {{W-|pa|گُوَانڈھی|n}}, {{W-|pa|گَوَانْڈَھݨ|m}}
* {{Z|kk}}: {{W-|kk|бастас}}, {{W-|kk|көрші}}, {{W-|kk|қоңсы}}
* {{Z|rm}}: {{W-|rm|vischin|n}}, {{W-|rm|vischina|m}}
* {{Z|ro}}: {{W+|ro|vecin|n}}, {{W+|ro|vecină|m}}
* {{Z|ru}}: {{W+|ru|сосе́д|n}}, {{W+|ru|сосе́дка|m}}
* {{Z|se}}: {{W-|se|ránnjá}}, {{W-|se|kránnjá}}
* {{Z|sa}}: {{W+|sa|प्रतिवेशिन्|n}}
* {{Z|sc}}: {{W-|sc|bichinu}}, {{W-|sc|bighinu}}, {{W-|sc|biginu}}, {{W-|sc|vichinu}}
* {{Z|ceb}}: {{W-|ceb|silingan}}
* {{Z|scn}}: {{W+|scn|vicinu}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|су́сед|n}}, {{W-|sh|су́сјед|n}}, {{W-|sh|суседкиња|m}}, {{W-|sh|сусетка|m}}, {{W-|sh|суседа|m}}, {{W-|sh|ко̀мшија|n}}, {{W-|sh|ко̀мшиница|m}}, {{W-|sh|комшикa|m}}
*: Latînî: {{W+|sh|súsed|n}}, {{W+|sh|súsjed|n}}, {{W-|sh|susedkinja|m}}, {{W-|sh|susetka|m}}, {{W-|sh|suseda|m}}, {{W+|sh|kòmšija|n}}, {{W+|sh|kòmšinica|m}}, {{W-|sh|komšika|m}}
* {{Z|sco}}: {{W-|sco|neibour}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|сѫсѣдъ|n}}
* {{Z|orv}}: {{W-|orv|сусѣдъ|n}}
* {{Z|sk}}: {{W-|sk|sused|n}}, {{W-|sk|susedka|m}}
* {{Z|sl}}: {{W+|sl|sosed|n}}, {{W-|sl|soseda|m}}
* {{Z|so}}: {{W+|so|jaar}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|sused|n}}
*: {{Z|hsb}}: {{W-|hsb|susod|n}}
* {{Z|es}}: {{W+|es|vecino|n}}, {{W+|es|vecina|m}}
* {{Z|sw}}: {{W+|sw|jirani|c9/10}}, {{W+|sw|majirani}}
* {{Z|sv}}: {{W+|sv|granne|g}}, {{W+|sv|nabo|g}}
* {{Z|zdj}}: {{W-|zdj|djirani|c5|c6}}
* {{Z|tab}}: {{W-|tab|гъунши}}
* {{Z|tg}}: {{W+|tg|ҳамсоя}}
* {{Z|tl}}: {{W+|tl|kapitbahay}}
* {{Z|ta}}: {{W+|ta|அண்டை}}
* {{Z|th}}: {{W+|th|เพื่อนบ้าน}}
* {{Z|te}}: {{W+|te|పొరుగు}}
* {{Z|crh}}: {{W-|crh|qomşu}}
* {{Z|tt}}: {{W+|tt|күрше}}
* {{Z|ti}}: {{W-|ti|ጐረቤት}}
* {{Z|tr}}: {{W+|tr|komşu}}
* {{Z|tk}}: {{W-|tk|goňşy}}
* {{Z|bo}}: {{W-|bo|གྲོང་པ}}, {{W-|bo|ཁྱིམ་མཚེས}}
* {{Z|tkn}}: {{W-|tkn|隣|tr=とぅない, tunai}}
* {{Z|tyv}}: {{W-|tyv|кадызы}}, {{W-|tyv|кожазы}}, {{W-|tyv|кожа}}
* {{Z|ur}}: {{W-|ur|ہَمْسایَہ|tr=hamsāya}}, {{W-|ur|پَڑوسی|n|tr=paṛosī}}, {{W-|ur|پَڑوسن|m|tr=paṛosin}}
* {{Z|uk}}: {{W+|uk|сусі́д|n}}, {{W+|uk|сусі́дка|m}}, {{W+|uk|сусі́да|g}}
* {{Z|vec}}: {{W-|vec|vesin}}, {{W-|vec|visin}}, {{W-|vec|vexin}}
* {{Z|vi}}: {{W+|vi|láng giềng}}, {{W+|vi|hàng xóm}}
* {{Z|vo}}: {{W+|vo|nilädan}}, {{W+|vo|hinilädan}}, {{W+|vo|jinilädan}}
* {{Z|wa}}: {{W+|wa|vijhén|n}}, {{W+|wa|vijhene|m}}
* {{Z|cy}}: {{W-|cy|cymydog|n}}
* {{Z|km}}: {{W-|km|[[អ្នក]][[ជិតខាង]]|tr=nĕək cɨt khaang}}, {{W-|km|[[មិត្ត]][[ជិតខាង]]|tr=mɨt cɨt khaang}}
* {{Z|rys}}: {{W-|rys|隣|tr=とぅなりぃ, tunarï}}
* {{Z|sah}}: {{W-|sah|ыал}}
* {{Z|yi}}: {{W-|yi|שכן|n|tr=shokhn}}, {{W-|yi|שאָכן|n}}
* {{Z|yoi}}: {{W-|yoi|隣|tr=とぅない, tunai}}
* {{Z|yox}}: {{W-|yox|隣|tr=とぅない, tunai}}
* {{Z|yo}}: {{W-|yo|aládùúgbò}}
* {{Z|el}}: {{W+|el|γείτονας|n}}, {{W+|el|γειτόνισσα|m}}
*: Kevn: {{W-|grc|γείτων|n}}
{{werger-bin}}
:{{werger çavkanî|en|neighbour|beş=Ev qismê Wergerê|oldid=75823214}}
== {{ziman|zza}} ==
=== Navdêr ===
{{navdêr|zza}}
# {{g|ku|cîran}}
#* {{bêmînak|zza}}
2hbqgqqjbls2cdoq7k6dcw1ymlyfx0w
6334128
6334127
2026-08-16T16:52:54Z
Wikihez
7344
Guhartinên [[Special:Contributions/~2026-42026-75|~2026-42026-75]] ([[User talk:~2026-42026-75|gotûbêj]]) hatine betalkirin, vegerand guhartoya dawî ya [[User:Balyozbot|Balyozbot]]
6304441
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{IPA|ku|/d͡ʒiːˈɾɑːn/}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-cîran.wav|dever=Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|c=mn}}
# [[kes|Kes]]ên [[mal]]ên wan [[nêz]]î [[hev]]; kesên li [[nêzîk]]î [[hev]] [[jîn|dijîn]].
#: {{hevmane|ku|dirawse|cînar|hevsa|hemsa|hevsî|hemsî}}
#: {{mk|ku|Em û ew '''cîran''' in.|Mala me û ya wan nêzî hev e.|birêz=1}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Mawer ke terh û bişkoyan</br>'''Cîranê''' berg û lû'lûyan</br>Ew 'er'er û selwa ciwan}}
#* {{jêder|ku|jêgirtin=Di nava ekradan de teklîf tunînin. Wekû tiştek lazim bibitin, û li mala '''cîranan''' ew tişt peyda bibitin, bilanî xweyî malê jî hazir nebitin, ew mirov bi diçitin û ew tiştê lazim buyî tînitin. |{{jêgirtin/Mele Mehmûdê Bayezîdî/Adat û Rusûmatnameê Ekradiye|r=125}}}}
# [[welat|Welat]]ên [[hevsinor]], [[dewlet]]ên ku [[tixûb]] bi hev re heye.
#: {{hevmane|ku|dirawse|cînar}}
#: {{nm|ku|Rûsya bi axa xwe welatê cîhanê yê herî mezin e û bi çardeh dewletan re '''cîran''' e.}}
#: {{mk|ku|Ereb, faris, ermenî û aşûrî '''cîranên''' me ne û yên herî kevin in, lê mixabin di bareya wêjeya wan de tiştekî nizanim ko bikaribim ya me û yên wan bidim ber hev.}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Bi alfabeyên din ====
* {{ku-ar|جيران}}
==== Formên alternatîv ====
* {{g|ku|cînar}}
==== Jê ====
{{stûn|
* {{mj|lêker}} {{g|ku|cîran bûn}}
* {{mj|lêker}} {{g|ku|cîran kirin}}
* {{mj|navdêr}} {{g|ku|cîranbûn}}
* {{mj|navdêr}} {{g|ku|cîrankirin}}
* {{mj|rengdêr}} {{g|ku|cîranbûyî}}
* {{mj|rengdêr}} {{g|ku|cîrankirî}}
* {{g|ku|cîranane}}
* {{g|ku|cîranî}}
* {{g|ku|cîranîtî}}
* {{g|ku|cîrantî}}
}}
==== Etîmolojî ====
Ji {{ji|ar|ku|جيران|tr=cîran}}, firejimar a {{g|ar|جار|tr=car|w=cîran, hevsî}}, ji lêkera {{g|ar|جار|tr=care|w=nêzîk bûn}}. Bi [[etîmolojiya gelêrî]] gelek caran têkil bi {{q|ku|cî}} tê kirin. Li pitirîna deveran ewa xwecih {{q|ku|hevsî}} ji cih kiriye, bide ber {{hv|ckb|دراوسێ|tr=[[dirawsê]]}} û {{hv|zza|embiriyane}}.
==== Werger ====
{{werger-ser}}
* {{Z|ab}}: {{W-|ab|агәыла}}
* {{Z|af}}: {{W-|af|buurman|n}}, {{W-|af|buurvrou|n}}
* {{Z|sq}}: {{W+|sq|fqinj|n}}, {{W-|sq|fqije|m}}, {{W-|sq|gjiton|n}}, {{W+|sq|komshi|n}}
* {{Z|goh}}: {{W-|goh|nāhgibūr}}
* {{Z|pdc}}: {{W-|pdc|Nochber}}
* {{Z|de}}: {{W+|de|Nachbar|n}}, {{W+|de|Nachbarin|m}}
* {{Z|ryn}}: {{W-|ryn|隣|tr=と゚なり, thonari}}
* {{Z|ams}}: {{W-|ams|隣|tr=とぅない, tunai}}
* {{Z|am}}: {{W-|am|ጎረቤት}}
* {{Z|arc}}:
*: Îbranî: {{W-|arc|שבבא|n|tr=šbābā’}}, {{W-|arc|שבבתא|m|tr=šbābtā’}}
*: Siryanî: {{W-|arc|ܫܒܒܐ|n|tr=šbābā’|}}, {{W-|arc|ܫܒܒܬܐ|m|tr=šbābtā’|}}
* {{Z|rup}}: {{W-|rup|vitsin|n}}
* {{Z|ast}}: {{W+|ast|vecín|n}}, {{W+|ast|vecina|m}}
* {{Z|az}}: {{W+|az|qonşu}}
* {{Z|eu}}: {{W-|eu|aldameneko}}, {{W-|eu|auzokide}}
* {{Z|ba}}: {{W-|ba|күрше}}
* {{Z|bar}}: {{W-|bar|Nochbar}}, {{W-|bar|Nåchbår}}
* {{Z|be}}: {{W-|be|сусе́д|n}}, {{W-|be|сусе́дка|m}}
* {{Z|bal}}: {{W-|bal|ہمسایگ|tr=hamsáyag}}, {{W-|bal|ہمساہگ|tr=hamsáhag, hamsáhig}}
* {{Z|bn}}: {{W-|bn|প্রতিবেশী}}
* {{Z|br}}: {{W+|br|amezeg|n}}, {{W+|br|amezegez|m}}
* {{Z|bg}}: {{W+|bg|съсе́д|n}}, {{W+|bg|съсе́дка|m}}, {{W+|bg|комши́я|n}}, {{W-|bg|комши́йка|m}}
* {{Z|my}}: {{W+|my|အိမ်နီးချင်း}}
* {{Z|bua}}: {{W-|bua|хүршэ}}
* {{Z|ce}}: {{W-|ce|лулахо}}
* {{Z|cs}}: {{W+|cs|soused|n}}, {{W+|cs|sousedka|m}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|鄰居|tr=leon4 geoi1}}
*: {{Z|dng}}: {{W-|dng|линҗү}}, {{W-|dng|гибир}}
*: {{Z|cmn}}: {{W-|cmn|鄰居|tr=línjū}}, {{W-|cmn|比鄰|tr=bǐlín, bìlín}}
*: {{Z|nan}}: {{W+|nan|厝邊|tr=chhù-piⁿ}}
* {{Z|dlm}}: {{W-|dlm|vičain|n}}
* {{Z|da}}: {{W+|da|nabo|g}}
* {{Z|ovd}}: {{W-|ovd|granne|n}}
* {{Z|id}}: {{W+|id|tetangga}}
* {{Z|ar}}: {{W+|ar|جَار|n}}, {{W+|ar|جَارة|m}}
*: {{Z|arz}}: {{W-|arz|جار|n|tr=gār}}, {{W-|arz|جارة|m|tr=gāra}}
*: {{Z|acw}}: {{W-|acw|جار|n|tr=jār}}, {{W-|acw|جارة|m|tr=jāra}}
*: {{Z|ary}}: {{W-|ary|جار|n|tr=jār}}, {{W-|ary|جارة|m|tr=jāra}}
* {{Z|hy}}: {{W-|hy|հարեւան}}, {{W+|hy|դրացի}}
* {{Z|eo}}: {{W+|eo|najbaro}}
* {{Z|et}}: {{W+|et|naaber}}
* {{Z|fa}}: {{W+|fa|همسایه|tr=hamsâye}}
* {{Z|fo}}: {{W-|fo|granni|n}}, {{W-|fo|nábúgvi|n}}
* {{Z|fi}}: {{W+|fi|naapuri}}
* {{Z|fr}}: {{W+|fr|voisin|n}}, {{W+|fr|voisine|m}}
* {{Z|fur}}: {{W-|fur|vicin|n}}, {{W-|fur|vicìn|n}}, {{W-|fur|prossim|n}}
* {{Z|frr}}: {{W-|frr|näiber}}
* {{Z|gd}}: {{W-|gd|nàbaidh|n}}
* {{Z|gl}}: {{W+|gl|veciño}}
* {{Z|got}}: {{W-|got|𐌱𐌹𐍃𐌹𐍄𐌰𐌽𐌳𐍃|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐌰|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐍉|m}}, {{W-|got|𐌽𐌴𐍈𐌿𐌽𐌳𐌾𐌰|n}}
* {{Z|ka}}: {{W+|ka|მეზობელი}}
* {{Z|mba}}: {{W-|mba|silingan}}
* {{Z|hi}}: {{W+|hi|पड़ोसी|n}}, {{W-|hi|पड़ोसिन|m}}, {{W+|hi|हमसाया|n}}
* {{Z|nl}}: {{W+|nl|buur|n}}, {{W+|nl|buurman|n}}, {{W+|nl|buurvrouw|m}}, {{W+|nl|buren|p}}, {{W+|nl|nabuur|n}}
* {{Z|he}}: {{W+|he|שָׁכֵן|n|tr=shakhén}}, {{W-|he|שְׁכֵנָה|m}}
* {{Z|io}}: {{W+|io|vicino}}, {{W-|io|vicinulo}}, {{W-|io|vicinino}}
* {{Z|ang}}: {{W-|ang|nīehsta|n}}, {{W-|ang|nēahġebūr|n}}
* {{Z|enm}}: {{W-|enm|neyghebour}}
* {{Z|en}}: {{W+|en|neighbour}}, {{W+|en|neighbor}}
* {{Z|ia}}: {{W-|ia|vicino}}
* {{Z|ga}}: {{W-|ga|comharsa|m}}
* {{Z|ist}}: {{W-|ist|viseîn|n}}
* {{Z|it}}: {{W+|it|vicino|n}}, {{W+|it|vicina|m}}
* {{Z|is}}: {{W+|is|nágranni|n}}, {{W+|is|nábúi|n}}
* {{Z|ja}}: {{W+|ja|隣人|tr=りんじん, rinjin}}, {{W-|ja|[[隣]]の[[人]]|tr=となりのひと, tonari no hito}}, {{W-|ja|[[近所]]の[[人]]|tr=きんじょのひと, kinjo no hito}}, {{W-|ja|[[隣家]]の[[人]]|tr=りんかのひと, rinka no hito}}
* {{Z|jje}}: {{W-|jje|이웃}}, {{W-|jje|윳}}
* {{Z|kl}}: {{W-|kl|sanileq}}, {{W-|kl|eqqaamiu}}
* {{Z|pam}}: {{W-|pam|siping-bale}}
* {{Z|ca}}: {{W+|ca|veí|n}}, {{W+|ca|veïna|m}}
* {{Z|kzg}}: {{W-|kzg|隣|tr=と゚ぅない, thunai}}
* {{Z|ky}}: {{W-|ky|коңшу}}, {{W-|ky|кошуна}}, {{W-|ky|көршу}}
* {{Z|ko}}: {{W-|ko|[[이웃]] [[사람]]}}, {{W+|ko|이웃}}
* {{Z|xug}}: {{W-|xug|隣|tr=とぅない, tunai}}
* {{Z|ltg}}: {{W-|ltg|kaimiņs|n}}, {{W-|ltg|sābrys|n}}
* {{Z|la}}: {{W-|la|adcola|n|m}}, {{W-|la|accola|n|m}}, {{W-|la|vīcīnus|n}}, {{W-|la|vīcīna|m}}
* {{Z|lv}}: {{W+|lv|kaimiņš|n}}, {{W-|lv|kaimiņiene|m}}
* {{Z|lo}}: {{W-|lo|ເພື່ອນບ້ານ}}
* {{Z|lt}}: {{W+|lt|kaimynas|n}}, {{W-|lt|kaimynė|m}}
* {{Z|lmo}}: {{W-|lmo|visin}}, {{W-|lmo|vesin}}
* {{Z|lb}}: {{W-|lb|Noper|n}}, {{W-|lb|Nopesch|m}}
* {{Z|mk}}: {{W+|mk|сосед|n}}, {{W-|mk|сосетка|m}}, {{W+|mk|комшија|n}}, {{W-|mk|комшика|m}}
* {{Z|ms}}: {{W+|ms|jiran}}, {{W-|ms|tetangga}}
* {{Z|mt}}: {{W-|mt|ġar|n}}
* {{Z|gv}}: {{W-|gv|naboo}}
* {{Z|mi}}: {{W-|mi|tangata-noho-tata}}, {{W-|mi|kiritata}}
* {{Z|hu}}: {{W+|hu|szomszéd}}
* {{Z|mvi}}: {{W-|mvi|隣|tr=とぅなㇲ゙, tunaz}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|хөрш}}
*: Mongolî: {{W-|mn|ᠬᠥᠷᠰᠢ}}
* {{Z|wlc}}: {{W-|wlc|djirani}}
* {{Z|nds}}: {{W-|nds|Nahwer|n}}
* {{Z|non}}: {{W-|non|granni|n}}, {{W-|non|nábúi}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|nabo|n}}
*: {{Z|nn}}: {{W-|nn|nabo|n}}
* {{Z|okn}}: {{W-|okn|隣|tr=とぅない, tunai}}
* {{Z|ryu}}: {{W-|ryu|隣|tr=とぅない, tunai}}
* {{Z|oc}}: {{W+|oc|vesin|n}}
* {{Z|om}}: {{W-|om|ollaa}}
* {{Z|ota}}: {{W-|ota|قونشو|tr=konşu, komşu}}
* {{Z|ug}}: {{W-|ug|قوشنا}}, {{W-|ug|خوشنا}}
* {{Z|uz}}: {{W+|uz|hamsoya}}, {{W+|uz|qoʻshni}}
* {{Z|pap}}: {{W-|pap|bisiña}}
* {{Z|ps}}: {{W-|ps|همسايه|m|tr=hamsāya}}, {{W-|ps|ګاونډی|n|tr=gāwanḍay}}
* {{Z|pms}}: {{W-|pms|vsin|n}}
* {{Z|pl}}: {{W+|pl|sąsiad|n}}, {{W+|pl|sąsiadka|m}}
* {{Z|pt}}: {{W+|pt|vizinho|n}}, {{W+|pt|vizinha|m}}
* Punjabi:
*: Gurmuxî: {{W-|pa|ਗੁਆਂਢੀ|n}}, {{W-|pa|ਗਵਾਂਢੀ|n}}, {{W-|pa|ਗੁਆਂਢਣ|m}}
*: Şahmuxî: {{W-|pa|گَوانڈھی|n}}, {{W-|pa|گُوَانڈھی|n}}, {{W-|pa|گَوَانْڈَھݨ|m}}
* {{Z|kk}}: {{W-|kk|бастас}}, {{W-|kk|көрші}}, {{W-|kk|қоңсы}}
* {{Z|rm}}: {{W-|rm|vischin|n}}, {{W-|rm|vischina|m}}
* {{Z|ro}}: {{W+|ro|vecin|n}}, {{W+|ro|vecină|m}}
* {{Z|ru}}: {{W+|ru|сосе́д|n}}, {{W+|ru|сосе́дка|m}}
* {{Z|se}}: {{W-|se|ránnjá}}, {{W-|se|kránnjá}}
* {{Z|sa}}: {{W+|sa|प्रतिवेशिन्|n}}
* {{Z|sc}}: {{W-|sc|bichinu}}, {{W-|sc|bighinu}}, {{W-|sc|biginu}}, {{W-|sc|vichinu}}
* {{Z|ceb}}: {{W-|ceb|silingan}}
* {{Z|scn}}: {{W+|scn|vicinu}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|су́сед|n}}, {{W-|sh|су́сјед|n}}, {{W-|sh|суседкиња|m}}, {{W-|sh|сусетка|m}}, {{W-|sh|суседа|m}}, {{W-|sh|ко̀мшија|n}}, {{W-|sh|ко̀мшиница|m}}, {{W-|sh|комшикa|m}}
*: Latînî: {{W+|sh|súsed|n}}, {{W+|sh|súsjed|n}}, {{W-|sh|susedkinja|m}}, {{W-|sh|susetka|m}}, {{W-|sh|suseda|m}}, {{W+|sh|kòmšija|n}}, {{W+|sh|kòmšinica|m}}, {{W-|sh|komšika|m}}
* {{Z|sco}}: {{W-|sco|neibour}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|сѫсѣдъ|n}}
* {{Z|orv}}: {{W-|orv|сусѣдъ|n}}
* {{Z|sk}}: {{W-|sk|sused|n}}, {{W-|sk|susedka|m}}
* {{Z|sl}}: {{W+|sl|sosed|n}}, {{W-|sl|soseda|m}}
* {{Z|so}}: {{W+|so|jaar}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|sused|n}}
*: {{Z|hsb}}: {{W-|hsb|susod|n}}
* {{Z|es}}: {{W+|es|vecino|n}}, {{W+|es|vecina|m}}
* {{Z|sw}}: {{W+|sw|jirani|c9/10}}, {{W+|sw|majirani}}
* {{Z|sv}}: {{W+|sv|granne|g}}, {{W+|sv|nabo|g}}
* {{Z|zdj}}: {{W-|zdj|djirani|c5|c6}}
* {{Z|tab}}: {{W-|tab|гъунши}}
* {{Z|tg}}: {{W+|tg|ҳамсоя}}
* {{Z|tl}}: {{W+|tl|kapitbahay}}
* {{Z|ta}}: {{W+|ta|அண்டை}}
* {{Z|th}}: {{W+|th|เพื่อนบ้าน}}
* {{Z|te}}: {{W+|te|పొరుగు}}
* {{Z|crh}}: {{W-|crh|qomşu}}
* {{Z|tt}}: {{W+|tt|күрше}}
* {{Z|ti}}: {{W-|ti|ጐረቤት}}
* {{Z|tr}}: {{W+|tr|komşu}}
* {{Z|tk}}: {{W-|tk|goňşy}}
* {{Z|bo}}: {{W-|bo|གྲོང་པ}}, {{W-|bo|ཁྱིམ་མཚེས}}
* {{Z|tkn}}: {{W-|tkn|隣|tr=とぅない, tunai}}
* {{Z|tyv}}: {{W-|tyv|кадызы}}, {{W-|tyv|кожазы}}, {{W-|tyv|кожа}}
* {{Z|ur}}: {{W-|ur|ہَمْسایَہ|tr=hamsāya}}, {{W-|ur|پَڑوسی|n|tr=paṛosī}}, {{W-|ur|پَڑوسن|m|tr=paṛosin}}
* {{Z|uk}}: {{W+|uk|сусі́д|n}}, {{W+|uk|сусі́дка|m}}, {{W+|uk|сусі́да|g}}
* {{Z|vec}}: {{W-|vec|vesin}}, {{W-|vec|visin}}, {{W-|vec|vexin}}
* {{Z|vi}}: {{W+|vi|láng giềng}}, {{W+|vi|hàng xóm}}
* {{Z|vo}}: {{W+|vo|nilädan}}, {{W+|vo|hinilädan}}, {{W+|vo|jinilädan}}
* {{Z|wa}}: {{W+|wa|vijhén|n}}, {{W+|wa|vijhene|m}}
* {{Z|cy}}: {{W-|cy|cymydog|n}}
* {{Z|km}}: {{W-|km|[[អ្នក]][[ជិតខាង]]|tr=nĕək cɨt khaang}}, {{W-|km|[[មិត្ត]][[ជិតខាង]]|tr=mɨt cɨt khaang}}
* {{Z|rys}}: {{W-|rys|隣|tr=とぅなりぃ, tunarï}}
* {{Z|sah}}: {{W-|sah|ыал}}
* {{Z|yi}}: {{W-|yi|שכן|n|tr=shokhn}}, {{W-|yi|שאָכן|n}}
* {{Z|yoi}}: {{W-|yoi|隣|tr=とぅない, tunai}}
* {{Z|yox}}: {{W-|yox|隣|tr=とぅない, tunai}}
* {{Z|yo}}: {{W-|yo|aládùúgbò}}
* {{Z|el}}: {{W+|el|γείτονας|n}}, {{W+|el|γειτόνισσα|m}}
*: Kevn: {{W-|grc|γείτων|n}}
{{werger-bin}}
:{{werger çavkanî|en|neighbour|beş=Ev qismê Wergerê|oldid=75823214}}
== {{ziman|zza}} ==
=== Navdêr ===
{{navdêr|zza}}
# {{g|ku|cîran}}
#* {{bêmînak|zza}}
ergdk3m7kjduexjqar3qcuhuo715cgs
6334177
6334128
2026-08-16T18:12:08Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir.)
6334177
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{IPA|ku|/d͡ʒiːˈɾɑːn/}}
* {{deng|ku|LL-Q36368 (kur)-Dildadil-cîran.wav|dever=Amed}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|c=mn}}
# [[kes|Kesên]] [[mal]]ên wan [[nêz]]î [[hev]]; kesên li [[nêzîk]]î [[hev]] [[jîn|dijîn]].
#: {{hevmane|ku|dirawse|cînar|hevsa|hemsa|hevsî|hemsî}}
#: {{mk|ku|Em û ew '''cîran''' in.|Mala me û ya wan nêzî hev e.|birêz=1}}
#* {{jêder|ku|{{Jêgirtin/Melayê Cizîrî/Dîwana Melayê Cizirî}}|jêgirtin=Mawer ke terh û bişkoyan</br>'''Cîranê''' berg û lû'lûyan</br>Ew 'er'er û selwa ciwan}}
#* {{jêder|ku|jêgirtin=Di nava ekradan de teklîf tunînin. Wekû tiştek lazim bibitin, û li mala '''cîranan''' ew tişt peyda bibitin, bilanî xweyî malê jî hazir nebitin, ew mirov bi diçitin û ew tiştê lazim buyî tînitin. |{{jêgirtin/Mele Mehmûdê Bayezîdî/Adat û Rusûmatnameê Ekradiye|r=125}}}}
# [[welat|Welatên]] [[hevsinor]], [[dewlet]]ên ku [[tixûb]] bi hev re heye.
#: {{hevmane|ku|dirawse|cînar}}
#: {{nm|ku|Rûsya bi axa xwe welatê cîhanê yê herî mezin e û bi çardeh dewletan re '''cîran''' e.}}
#: {{mk|ku|Ereb, faris, ermenî û aşûrî '''cîranên''' me ne û yên herî kevin in, lê mixabin di bareya wêjeya wan de tiştekî nizanim ko bikaribim ya me û yên wan bidim ber hev.}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Bi alfabeyên din ====
* {{ku-ar|جيران}}
==== Formên alternatîv ====
* {{g|ku|cînar}}
==== Jê ====
{{stûn|
* {{mj|lêker}} {{g|ku|cîran bûn}}
* {{mj|lêker}} {{g|ku|cîran kirin}}
* {{mj|navdêr}} {{g|ku|cîranbûn}}
* {{mj|navdêr}} {{g|ku|cîrankirin}}
* {{mj|rengdêr}} {{g|ku|cîranbûyî}}
* {{mj|rengdêr}} {{g|ku|cîrankirî}}
* {{g|ku|cîranane}}
* {{g|ku|cîranî}}
* {{g|ku|cîranîtî}}
* {{g|ku|cîrantî}}
}}
==== Etîmolojî ====
Ji {{ji|ar|ku|جيران|tr=cîran}}, firejimar a {{g|ar|جار|tr=car|w=cîran, hevsî}}, ji lêkera {{g|ar|جار|tr=care|w=nêzîk bûn}}. Bi [[etîmolojiya gelêrî]] gelek caran têkil bi {{q|ku|cî}} tê kirin. Li pitirîna deveran ewa xwecih {{q|ku|hevsî}} ji cih kiriye, bide ber {{hv|ckb|دراوسێ|tr=[[dirawsê]]}} û {{hv|zza|embiriyane}}.
==== Werger ====
{{werger-ser}}
* {{Z|ab}}: {{W-|ab|агәыла}}
* {{Z|af}}: {{W-|af|buurman|n}}, {{W-|af|buurvrou|n}}
* {{Z|sq}}: {{W+|sq|fqinj|n}}, {{W-|sq|fqije|m}}, {{W-|sq|gjiton|n}}, {{W+|sq|komshi|n}}
* {{Z|goh}}: {{W-|goh|nāhgibūr}}
* {{Z|pdc}}: {{W-|pdc|Nochber}}
* {{Z|de}}: {{W+|de|Nachbar|n}}, {{W+|de|Nachbarin|m}}
* {{Z|ryn}}: {{W-|ryn|隣|tr=と゚なり, thonari}}
* {{Z|ams}}: {{W-|ams|隣|tr=とぅない, tunai}}
* {{Z|am}}: {{W-|am|ጎረቤት}}
* {{Z|arc}}:
*: Îbranî: {{W-|arc|שבבא|n|tr=šbābā’}}, {{W-|arc|שבבתא|m|tr=šbābtā’}}
*: Siryanî: {{W-|arc|ܫܒܒܐ|n|tr=šbābā’|}}, {{W-|arc|ܫܒܒܬܐ|m|tr=šbābtā’|}}
* {{Z|rup}}: {{W-|rup|vitsin|n}}
* {{Z|ast}}: {{W+|ast|vecín|n}}, {{W+|ast|vecina|m}}
* {{Z|az}}: {{W+|az|qonşu}}
* {{Z|eu}}: {{W-|eu|aldameneko}}, {{W-|eu|auzokide}}
* {{Z|ba}}: {{W-|ba|күрше}}
* {{Z|bar}}: {{W-|bar|Nochbar}}, {{W-|bar|Nåchbår}}
* {{Z|be}}: {{W-|be|сусе́д|n}}, {{W-|be|сусе́дка|m}}
* {{Z|bal}}: {{W-|bal|ہمسایگ|tr=hamsáyag}}, {{W-|bal|ہمساہگ|tr=hamsáhag, hamsáhig}}
* {{Z|bn}}: {{W-|bn|প্রতিবেশী}}
* {{Z|br}}: {{W+|br|amezeg|n}}, {{W+|br|amezegez|m}}
* {{Z|bg}}: {{W+|bg|съсе́д|n}}, {{W+|bg|съсе́дка|m}}, {{W+|bg|комши́я|n}}, {{W-|bg|комши́йка|m}}
* {{Z|my}}: {{W+|my|အိမ်နီးချင်း}}
* {{Z|bua}}: {{W-|bua|хүршэ}}
* {{Z|ce}}: {{W-|ce|лулахо}}
* {{Z|cs}}: {{W+|cs|soused|n}}, {{W+|cs|sousedka|m}}
* {{Z|zh}}:
*: {{Z|yue}}: {{W-|yue|鄰居|tr=leon4 geoi1}}
*: {{Z|dng}}: {{W-|dng|линҗү}}, {{W-|dng|гибир}}
*: {{Z|cmn}}: {{W-|cmn|鄰居|tr=línjū}}, {{W-|cmn|比鄰|tr=bǐlín, bìlín}}
*: {{Z|nan}}: {{W+|nan|厝邊|tr=chhù-piⁿ}}
* {{Z|dlm}}: {{W-|dlm|vičain|n}}
* {{Z|da}}: {{W+|da|nabo|g}}
* {{Z|ovd}}: {{W-|ovd|granne|n}}
* {{Z|id}}: {{W+|id|tetangga}}
* {{Z|ar}}: {{W+|ar|جَار|n}}, {{W+|ar|جَارة|m}}
*: {{Z|arz}}: {{W-|arz|جار|n|tr=gār}}, {{W-|arz|جارة|m|tr=gāra}}
*: {{Z|acw}}: {{W-|acw|جار|n|tr=jār}}, {{W-|acw|جارة|m|tr=jāra}}
*: {{Z|ary}}: {{W-|ary|جار|n|tr=jār}}, {{W-|ary|جارة|m|tr=jāra}}
* {{Z|hy}}: {{W-|hy|հարեւան}}, {{W+|hy|դրացի}}
* {{Z|eo}}: {{W+|eo|najbaro}}
* {{Z|et}}: {{W+|et|naaber}}
* {{Z|fa}}: {{W+|fa|همسایه|tr=hamsâye}}
* {{Z|fo}}: {{W-|fo|granni|n}}, {{W-|fo|nábúgvi|n}}
* {{Z|fi}}: {{W+|fi|naapuri}}
* {{Z|fr}}: {{W+|fr|voisin|n}}, {{W+|fr|voisine|m}}
* {{Z|fur}}: {{W-|fur|vicin|n}}, {{W-|fur|vicìn|n}}, {{W-|fur|prossim|n}}
* {{Z|frr}}: {{W-|frr|näiber}}
* {{Z|gd}}: {{W-|gd|nàbaidh|n}}
* {{Z|gl}}: {{W+|gl|veciño}}
* {{Z|got}}: {{W-|got|𐌱𐌹𐍃𐌹𐍄𐌰𐌽𐌳𐍃|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐌰|n}}, {{W-|got|𐌲𐌰𐍂𐌰𐌶𐌽𐍉|m}}, {{W-|got|𐌽𐌴𐍈𐌿𐌽𐌳𐌾𐌰|n}}
* {{Z|ka}}: {{W+|ka|მეზობელი}}
* {{Z|mba}}: {{W-|mba|silingan}}
* {{Z|hi}}: {{W+|hi|पड़ोसी|n}}, {{W-|hi|पड़ोसिन|m}}, {{W+|hi|हमसाया|n}}
* {{Z|nl}}: {{W+|nl|buur|n}}, {{W+|nl|buurman|n}}, {{W+|nl|buurvrouw|m}}, {{W+|nl|buren|p}}, {{W+|nl|nabuur|n}}
* {{Z|he}}: {{W+|he|שָׁכֵן|n|tr=shakhén}}, {{W-|he|שְׁכֵנָה|m}}
* {{Z|io}}: {{W+|io|vicino}}, {{W-|io|vicinulo}}, {{W-|io|vicinino}}
* {{Z|ang}}: {{W-|ang|nīehsta|n}}, {{W-|ang|nēahġebūr|n}}
* {{Z|enm}}: {{W-|enm|neyghebour}}
* {{Z|en}}: {{W+|en|neighbour}}, {{W+|en|neighbor}}
* {{Z|ia}}: {{W-|ia|vicino}}
* {{Z|ga}}: {{W-|ga|comharsa|m}}
* {{Z|ist}}: {{W-|ist|viseîn|n}}
* {{Z|it}}: {{W+|it|vicino|n}}, {{W+|it|vicina|m}}
* {{Z|is}}: {{W+|is|nágranni|n}}, {{W+|is|nábúi|n}}
* {{Z|ja}}: {{W+|ja|隣人|tr=りんじん, rinjin}}, {{W-|ja|[[隣]]の[[人]]|tr=となりのひと, tonari no hito}}, {{W-|ja|[[近所]]の[[人]]|tr=きんじょのひと, kinjo no hito}}, {{W-|ja|[[隣家]]の[[人]]|tr=りんかのひと, rinka no hito}}
* {{Z|jje}}: {{W-|jje|이웃}}, {{W-|jje|윳}}
* {{Z|kl}}: {{W-|kl|sanileq}}, {{W-|kl|eqqaamiu}}
* {{Z|pam}}: {{W-|pam|siping-bale}}
* {{Z|ca}}: {{W+|ca|veí|n}}, {{W+|ca|veïna|m}}
* {{Z|kzg}}: {{W-|kzg|隣|tr=と゚ぅない, thunai}}
* {{Z|ky}}: {{W-|ky|коңшу}}, {{W-|ky|кошуна}}, {{W-|ky|көршу}}
* {{Z|ko}}: {{W-|ko|[[이웃]] [[사람]]}}, {{W+|ko|이웃}}
* {{Z|xug}}: {{W-|xug|隣|tr=とぅない, tunai}}
* {{Z|ltg}}: {{W-|ltg|kaimiņs|n}}, {{W-|ltg|sābrys|n}}
* {{Z|la}}: {{W-|la|adcola|n|m}}, {{W-|la|accola|n|m}}, {{W-|la|vīcīnus|n}}, {{W-|la|vīcīna|m}}
* {{Z|lv}}: {{W+|lv|kaimiņš|n}}, {{W-|lv|kaimiņiene|m}}
* {{Z|lo}}: {{W-|lo|ເພື່ອນບ້ານ}}
* {{Z|lt}}: {{W+|lt|kaimynas|n}}, {{W-|lt|kaimynė|m}}
* {{Z|lmo}}: {{W-|lmo|visin}}, {{W-|lmo|vesin}}
* {{Z|lb}}: {{W-|lb|Noper|n}}, {{W-|lb|Nopesch|m}}
* {{Z|mk}}: {{W+|mk|сосед|n}}, {{W-|mk|сосетка|m}}, {{W+|mk|комшија|n}}, {{W-|mk|комшика|m}}
* {{Z|ms}}: {{W+|ms|jiran}}, {{W-|ms|tetangga}}
* {{Z|mt}}: {{W-|mt|ġar|n}}
* {{Z|gv}}: {{W-|gv|naboo}}
* {{Z|mi}}: {{W-|mi|tangata-noho-tata}}, {{W-|mi|kiritata}}
* {{Z|hu}}: {{W+|hu|szomszéd}}
* {{Z|mvi}}: {{W-|mvi|隣|tr=とぅなㇲ゙, tunaz}}
* {{Z|mn}}:
*: Kirîlî: {{W+|mn|хөрш}}
*: Mongolî: {{W-|mn|ᠬᠥᠷᠰᠢ}}
* {{Z|wlc}}: {{W-|wlc|djirani}}
* {{Z|nds}}: {{W-|nds|Nahwer|n}}
* {{Z|non}}: {{W-|non|granni|n}}, {{W-|non|nábúi}}
* {{Z|no}}:
*: {{Z|nb}}: {{W+|nb|nabo|n}}
*: {{Z|nn}}: {{W-|nn|nabo|n}}
* {{Z|okn}}: {{W-|okn|隣|tr=とぅない, tunai}}
* {{Z|ryu}}: {{W-|ryu|隣|tr=とぅない, tunai}}
* {{Z|oc}}: {{W+|oc|vesin|n}}
* {{Z|om}}: {{W-|om|ollaa}}
* {{Z|ota}}: {{W-|ota|قونشو|tr=konşu, komşu}}
* {{Z|ug}}: {{W-|ug|قوشنا}}, {{W-|ug|خوشنا}}
* {{Z|uz}}: {{W+|uz|hamsoya}}, {{W+|uz|qoʻshni}}
* {{Z|pap}}: {{W-|pap|bisiña}}
* {{Z|ps}}: {{W-|ps|همسايه|m|tr=hamsāya}}, {{W-|ps|ګاونډی|n|tr=gāwanḍay}}
* {{Z|pms}}: {{W-|pms|vsin|n}}
* {{Z|pl}}: {{W+|pl|sąsiad|n}}, {{W+|pl|sąsiadka|m}}
* {{Z|pt}}: {{W+|pt|vizinho|n}}, {{W+|pt|vizinha|m}}
* Punjabi:
*: Gurmuxî: {{W-|pa|ਗੁਆਂਢੀ|n}}, {{W-|pa|ਗਵਾਂਢੀ|n}}, {{W-|pa|ਗੁਆਂਢਣ|m}}
*: Şahmuxî: {{W-|pa|گَوانڈھی|n}}, {{W-|pa|گُوَانڈھی|n}}, {{W-|pa|گَوَانْڈَھݨ|m}}
* {{Z|kk}}: {{W-|kk|бастас}}, {{W-|kk|көрші}}, {{W-|kk|қоңсы}}
* {{Z|rm}}: {{W-|rm|vischin|n}}, {{W-|rm|vischina|m}}
* {{Z|ro}}: {{W+|ro|vecin|n}}, {{W+|ro|vecină|m}}
* {{Z|ru}}: {{W+|ru|сосе́д|n}}, {{W+|ru|сосе́дка|m}}
* {{Z|se}}: {{W-|se|ránnjá}}, {{W-|se|kránnjá}}
* {{Z|sa}}: {{W+|sa|प्रतिवेशिन्|n}}
* {{Z|sc}}: {{W-|sc|bichinu}}, {{W-|sc|bighinu}}, {{W-|sc|biginu}}, {{W-|sc|vichinu}}
* {{Z|ceb}}: {{W-|ceb|silingan}}
* {{Z|scn}}: {{W+|scn|vicinu}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|су́сед|n}}, {{W-|sh|су́сјед|n}}, {{W-|sh|суседкиња|m}}, {{W-|sh|сусетка|m}}, {{W-|sh|суседа|m}}, {{W-|sh|ко̀мшија|n}}, {{W-|sh|ко̀мшиница|m}}, {{W-|sh|комшикa|m}}
*: Latînî: {{W+|sh|súsed|n}}, {{W+|sh|súsjed|n}}, {{W-|sh|susedkinja|m}}, {{W-|sh|susetka|m}}, {{W-|sh|suseda|m}}, {{W+|sh|kòmšija|n}}, {{W+|sh|kòmšinica|m}}, {{W-|sh|komšika|m}}
* {{Z|sco}}: {{W-|sco|neibour}}
* {{Z|cu}}:
*: Kirîlî: {{W-|cu|сѫсѣдъ|n}}
* {{Z|orv}}: {{W-|orv|сусѣдъ|n}}
* {{Z|sk}}: {{W-|sk|sused|n}}, {{W-|sk|susedka|m}}
* {{Z|sl}}: {{W+|sl|sosed|n}}, {{W-|sl|soseda|m}}
* {{Z|so}}: {{W+|so|jaar}}
* Sorbî:
*: {{Z|dsb}}: {{W-|dsb|sused|n}}
*: {{Z|hsb}}: {{W-|hsb|susod|n}}
* {{Z|es}}: {{W+|es|vecino|n}}, {{W+|es|vecina|m}}
* {{Z|sw}}: {{W+|sw|jirani|c9/10}}, {{W+|sw|majirani}}
* {{Z|sv}}: {{W+|sv|granne|g}}, {{W+|sv|nabo|g}}
* {{Z|zdj}}: {{W-|zdj|djirani|c5|c6}}
* {{Z|tab}}: {{W-|tab|гъунши}}
* {{Z|tg}}: {{W+|tg|ҳамсоя}}
* {{Z|tl}}: {{W+|tl|kapitbahay}}
* {{Z|ta}}: {{W+|ta|அண்டை}}
* {{Z|th}}: {{W+|th|เพื่อนบ้าน}}
* {{Z|te}}: {{W+|te|పొరుగు}}
* {{Z|crh}}: {{W-|crh|qomşu}}
* {{Z|tt}}: {{W+|tt|күрше}}
* {{Z|ti}}: {{W-|ti|ጐረቤት}}
* {{Z|tr}}: {{W+|tr|komşu}}
* {{Z|tk}}: {{W-|tk|goňşy}}
* {{Z|bo}}: {{W-|bo|གྲོང་པ}}, {{W-|bo|ཁྱིམ་མཚེས}}
* {{Z|tkn}}: {{W-|tkn|隣|tr=とぅない, tunai}}
* {{Z|tyv}}: {{W-|tyv|кадызы}}, {{W-|tyv|кожазы}}, {{W-|tyv|кожа}}
* {{Z|ur}}: {{W-|ur|ہَمْسایَہ|tr=hamsāya}}, {{W-|ur|پَڑوسی|n|tr=paṛosī}}, {{W-|ur|پَڑوسن|m|tr=paṛosin}}
* {{Z|uk}}: {{W+|uk|сусі́д|n}}, {{W+|uk|сусі́дка|m}}, {{W+|uk|сусі́да|g}}
* {{Z|vec}}: {{W-|vec|vesin}}, {{W-|vec|visin}}, {{W-|vec|vexin}}
* {{Z|vi}}: {{W+|vi|láng giềng}}, {{W+|vi|hàng xóm}}
* {{Z|vo}}: {{W+|vo|nilädan}}, {{W+|vo|hinilädan}}, {{W+|vo|jinilädan}}
* {{Z|wa}}: {{W+|wa|vijhén|n}}, {{W+|wa|vijhene|m}}
* {{Z|cy}}: {{W-|cy|cymydog|n}}
* {{Z|km}}: {{W-|km|[[អ្នក]][[ជិតខាង]]|tr=nĕək cɨt khaang}}, {{W-|km|[[មិត្ត]][[ជិតខាង]]|tr=mɨt cɨt khaang}}
* {{Z|rys}}: {{W-|rys|隣|tr=とぅなりぃ, tunarï}}
* {{Z|sah}}: {{W-|sah|ыал}}
* {{Z|yi}}: {{W-|yi|שכן|n|tr=shokhn}}, {{W-|yi|שאָכן|n}}
* {{Z|yoi}}: {{W-|yoi|隣|tr=とぅない, tunai}}
* {{Z|yox}}: {{W-|yox|隣|tr=とぅない, tunai}}
* {{Z|yo}}: {{W-|yo|aládùúgbò}}
* {{Z|el}}: {{W+|el|γείτονας|n}}, {{W+|el|γειτόνισσα|m}}
*: Kevn: {{W-|grc|γείτων|n}}
{{werger-bin}}
:{{werger çavkanî|en|neighbour|beş=Ev qismê Wergerê|oldid=75823214}}
== {{ziman|zza}} ==
=== Navdêr ===
{{navdêr|zza}}
# {{g|ku|cîran}}
#* {{bêmînak|zza}}
q2vjc1xp7e67m4vj5vwmkvqr6r4dh0j
yapmak
0
22197
6334114
6068499
2026-08-16T15:10:43Z
Wikihez
7344
/* Lêker */ test [[Special:PermanentLink/6334112#c-Wikihez-20260816150600-Ghybu-20260816150200|bnr.]]
6334114
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Bilêvkirin ===
* {{deng|tr|LL-Q256 (tur)-ToprakM-yapmak.wav|Deng|dever=}}
=== Lêker ===
{{tr-tew-lêker}}
{{lêker|tr|fniha=yapıyorum|fborî=yaptı|fsubj=yap}}
# [[çêkirin]], [[kirin]].
#* {{bêmînak|tr}}
==== Jê ====
{{kol3|tr
|yapma
|yapı
|yapılma
|yapılmak
|yapılmış
|yapım
|yapımcı
|yapıt
}}
kt966eafkk9phekh3xmmf8a8x7rvzbs
cehêl
0
24617
6334284
6132162
2026-08-17T04:52:36Z
~2026-45013-89
15262
/* Etîmolojî */
6334284
wikitext
text/x-wiki
{{ds|cihêl|cehêl}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[genc]], [[ciwan]] (bi taybetî [[keç]], ne [[kurr]])
#* {{bêmînak|ku}}
==== Têkildar ====
* [[cahil]]
==== Etîmolojî ====
du boçon hene;{{ji|ar|جاھل: nezan, xişîm}}an {{ji: ci/ce? û hêl: hêz}}
==== Jê ====
* [[cehêlî]]
* [[cehêlîtî]]
* [[cehêltî]]
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Jüngling|m|guman=erê}}
* {{Z|fa}}: {{W+|fa|جوان}}, {{W+|fa|نادان}}
* {{Z|en}}: {{W+|en|youth}}
{{werger-bin}}
45zzqpkrb50pypkp86yksl4v1sup8tp
6334285
6334284
2026-08-17T05:41:06Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334285
wikitext
text/x-wiki
{{ds|cihêl|cehêl}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[genc]], [[ciwan]] (bi taybetî [[keç]], ne [[kurr]])
#* {{bêmînak|ku}}
==== Jê ====
* [[cehêlî]]
* [[cehêlîtî]]
* [[cehêltî]]
==== Têkildar ====
* [[cahil]]
==== Etîmolojî ====
du boçon hene;{{ji|ar|جاھل: nezan, xişîm}}an {{ji: ci/ce? û hêl: hêz}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Jüngling|m|guman=erê}}
* {{Z|fa}}: {{W+|fa|جوان}}, {{W+|fa|نادان}}
* {{Z|en}}: {{W+|en|youth}}
{{werger-bin}}
8ufeou5eoy6th6ke0v9c30mezytn4p3
6334305
6334285
2026-08-17T11:54:22Z
Wikihez
7344
/* Etîmolojî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334305
wikitext
text/x-wiki
{{ds|cihêl|cehêl}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Rengdêr ===
{{rengdêr|ku}}
# [[genc]], [[ciwan]] (bi taybetî [[keç]], ne [[kurr]])
#* {{bêmînak|ku}}
==== Jê ====
* [[cehêlî]]
* [[cehêlîtî]]
* [[cehêltî]]
==== Têkildar ====
* [[cahil]]
==== Etîmolojî ====
{{ji?|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Jüngling|m|guman=erê}}
* {{Z|fa}}: {{W+|fa|جوان}}, {{W+|fa|نادان}}
* {{Z|en}}: {{W+|en|youth}}
{{werger-bin}}
1ivcqt2d8t4dsypley5rd8opf77hm55
muzîsyen
0
31743
6334294
6312732
2026-08-17T09:21:19Z
Wikihez
7344
6334294
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=mn}}
# Kesa/ê ku [[kar]]ê wî yan [[mijûlî|mijûliya]] wî [[muzîk]] e.
#: {{hevmane|ku|muzîkvan|muzîkjen|muzîkkar}}
#* {{nimûne?|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Formên alternatîv ====
* {{g|ku|muzisyen}}
* {{g|ku|mûzisyen}}
* {{g|ku|mûzîsyen}}
==== Jê ====
* {{g|ku|muzîsyenî}}
* {{g|ku|muzîsyenîtî}}
* {{g|ku|muzîsyentî}}
==== Têkildar ====
* {{g|ku|bilûrvan}}
* {{g|ku|deholvan}}
* {{g|ku|sazvan}}
* {{g|ku|tembûrvan}}
* {{g|ku|zirrnevan}}
==== Etîmolojî ====
Ji {{ji|fr|ku|musicien}}.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|musikant}}, {{W-|af|musikus}}, {{W+|af|musici|p}}
* {{Z|sq}}: {{W+|sq|muzikant|n}}
* {{Z|de}}: {{W+|de|Musiker|n}}, {{W+|de|Musikant|n}}, {{W+|de|Tonkünstler|n}}, {{W+|de|Musikus|n}}
* {{Z|ast}}: {{W-|ast|músicu|n}}
* {{Z|az}}: {{W-|az|musiqiçi}}
* {{Z|eu}}: {{W-|eu|musikari}}
* {{Z|be}}: {{W-|be|музы́ка|n|sc=Cyrl}}, {{W-|be|музыка́нт|n|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|বাদক}}
* {{Z|br}}: {{W+|br|soner|n}}, {{W-|br|sonour|n}}, {{W-|br|muzisian|n}}, {{W-|br|muziker|n}}
* {{Z|bg}}: {{W-|bg|музика́нт|n}}
* {{Z|my}}: {{W+|my|ဂီတသမား|sc=Mymr}}
* {{Z|ce}}: {{W-|ce|музыкант}}
* {{Z|cs}}: {{W+|cs|hudebník|n}}, {{W+|cs|muzikant|n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|音樂家|tr=yīnyuèjiā}}
* {{Z|ckt}}: {{W-|ckt|эʼйӈэнэӈэткульын|tr=ėʼjṇėnėṇėtkulʹyn}}
* {{Z|da}}: {{W+|da|musiker|g}}
* {{Z|id}}: {{W+|id|musikus}}, {{W+|id|pemusik}}
* {{Z|ar}}: {{W+|ar|مُوسِيقِيّ|n}}
* {{Z|hy}}: {{W+|hy|երաժիշտ}}
* {{Z|eo}}: {{W+|eo|muzikisto}}
* {{Z|et}}: {{W+|et|muusik}}
* {{Z|fa}}: {{W+|fa|موزیسین|tr=muzisian}}, {{W-|fa|موزیکان|tr=muzikân}}, {{W+|fa|موسیقیدان|tr=musiqidân}}
* {{Z|fi}}: {{W+|fi|muusikko}}
* {{Z|fr}}: {{W+|fr|musicien|n}}, {{W+|fr|musicienne|m}}
* {{Z|fy}}: {{W-|fy|muzikus|n|m}}, {{W-|fy|muzikant|n}}, {{W-|fy|muzikante|m}}, {{W-|fy|muzykman|n}}
* {{Z|gd}}: {{W-|gd|ceòladair|n}}, {{W-|gd|neach-ciùil|n}}
* {{Z|gl}}: {{W+|gl|músico|n}}, {{W+|gl|música|m}}
* {{Z|ka}}: {{W-|ka|მუსიკოსი}}
* {{Z|hi}}: {{W+|hi|संगीतकार|n}}
* {{Z|nl}}: {{W+|nl|muzikant|n}}, {{W+|nl|muzikante|m}}, {{W+|nl|musicus|n|m}}
* {{Z|he}}: {{W-|he|מוּזִיקַאי|tr=muzikai}}
* {{Z|ang}}: {{W-|ang|dreāmere|n}}
* {{Z|enm}}: {{W-|enm|musicien}}, {{W-|enm|musiker}}, {{W-|enm|synger}}
* {{Z|en}}: {{W+|en|musician}}
* {{Z|ga}}: {{W-|ga|ceoltóir|n}}
* {{Z|it}}: {{W+|it|musicista|n|m}}, {{W+|it|musico|n}}
* {{Z|is}}: {{W+|is|tónlistarmaður|n}}
* {{Z|ja}}: {{W+|ja|音楽家|tr=おんがくか, ongakuka}}
* {{Z|jv}}: {{W-|jv|niyógo}}
* {{Z|kl}}: {{W-|kl|nipilersortartoq}}
* {{Z|xal}}: {{W-|xal|көгҗмч|sc=Cyrl}}
* {{Z|ca}}: {{W+|ca|músic|n}}
* {{Z|ky}}: {{W+|ky|музыкант|sc=Cyrl}}
* {{Z|ko}}: {{W+|ko|음악가}}
* {{Z|kw}}: {{W-|kw|ilewydh|n}}, {{W-|kw|ilewydhes|m}}
* {{Z|co}}: {{W-|co|musicante|n|m}}
* {{Z|la}}: {{W-|la|mūsicus|n}}, {{W-|la|mūsica|m}}
* {{Z|lv}}: {{W-|lv|mūziķis|n}}
* {{Z|lo}}: {{W+|lo|ນັກດົນຕີ}}
* {{Z|lij}}: {{W-|lij|mûxicante|n}}
* {{Z|lt}}: {{W-|lt|muzikantas|n}}, {{W-|lt|muzikas|n}}
* {{Z|lmo}}: {{W-|lmo|musicista|n}}
* {{Z|lb}}: {{W-|lb|Museker|n}}, {{W-|lb|Musekerin|m}}
* {{Z|mk}}: {{W-|mk|му́зичар|n}}
* {{Z|ms}}: {{W-|ms|pemuzik}}
* {{Z|mi}}: {{W-|mi|kaiwhakatangi}}, {{W+|mi|kai}}, {{W-|mi|kaipuoro}}, {{W-|mi|kiripuoro}}
* {{Z|hu}}: {{W+|hu|zenész}}, {{W+|hu|muzsikus}}
* {{Z|mn}}: {{W+|mn|хөгжимчин|sc=Cyrl}}
* {{Z|nhg}}: {{W-|nhg|mursi̱co}}
* {{Z|nrf}}: {{W-|nrf|musicien|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W-|nb|musiker|n}}
*: {{Z|nn}}: {{W-|nn|musikar|n}}
* {{Z|uz}}: {{W+|uz|musiqachi}}, {{W+|uz|muzikant}}
* {{Z|pi}}: {{W-|pi|vādaka|n}}
* {{Z|pms}}: {{W-|pms|musicista|n}}
* {{Z|pl}}: {{W+|pl|muzyk|m-pr}}, {{W+|pl|muzyczka|m}}
* {{Z|pt}}: {{W+|pt|músico|n}}
* {{Z|kk}}: {{W-|kk|музыкант|sc=Cyrl}}
* {{Z|rgn}}: {{W-|rgn|mușicânt|n}}
* {{Z|ro}}: {{W+|ro|muzician|n}}, {{W+|ro|muziciană|m}}
* {{Z|ru}}: {{W-|ru|музыка́нт|n}}, {{W-|ru|музыка́нтка|m|sc=Cyrl}}
* {{Z|sa}}: {{W+|sa|वादक|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|музичар|n|sc=Cyrl}}
*: Latînî: {{W+|sh|muzičar|n}}, {{W+|sh|glazbenik|n}}
* {{Z|sco}}: {{W-|sco|muisicker}}, {{W-|sco|muisician}}
* {{Z|sk}}: {{W-|sk|hudobník|n}}, {{W-|sk|muzikant|n}}
* {{Z|sl}}: {{W+|sl|glasbenik|n}}, {{W-|sl|glasbenica|m}}
* {{Z|st}}: {{W-|st|sebini}}
* {{Z|es}}: {{W+|es|músico|n}}
* {{Z|sw}}: {{W+|sw|mwimbaji}}, {{W+|sw|mwanamuziki}}
* {{Z|sv}}: {{W+|sv|musikant|g}}, {{W+|sv|musiker|g}}
* {{Z|tg}}: {{W-|tg|мусиқачӣ}}
* {{Z|tl}}: {{W-|tl|musiko}}
* {{Z|rif}}: {{W-|rif|amedyaz|n}}
* {{Z|th}}: {{W-|th|นักดนตรี}}
* {{Z|tr}}: {{W+|tr|müzisyen}}
* {{Z|tk}}: {{W-|tk|sazandar}}, {{W-|tk|sazanda}}
* {{Z|ur}}: {{W-|ur|موسیقار|n|tr=musiqār|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|музика́нт|n}}
* {{Z|vi}}: {{W+|vi|nhạc sĩ}}
* {{Z|vo}}: {{W+|vo|musigan}}
* {{Z|cy}}: {{W+|cy|cerddwr|n}}
* {{Z|km}}: {{W-|km|អ្នកភ្លេង}}, {{W+|km|វាទកៈ}}
* {{Z|yi}}: {{W-|yi|מוזיקער|n}}
* {{Z|el}}: {{W+|el|μουσικός|n|m}}
*: Kevn: {{W-|grc|μουσικός|n}}
{{werger-bin}}
:{{werger çavkanî|en|musician|beş=Ev qismê Wergerê|oldid=75678903}}
{{mijar|ku|muzîsyen|kes}}
mrbv3ay0i5lugu8f2c8fovnqzl2g8xw
6334295
6334294
2026-08-17T09:22:14Z
Wikihez
7344
+nimûneya bikaranînê bi amûra « [[Mediawiki:Gadget-wikt.add-examples|Nimûne zêde bike]] » (v1.7).
6334295
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=mn}}
# Kesa/ê ku [[kar]]ê wî yan [[mijûlî|mijûliya]] wî [[muzîk]] e.
#: {{hevmane|ku|muzîkvan|muzîkjen|muzîkkar}}
#* {{jêder-websîte|ku|nivîskar=Jan Dost|sernav=جان دۆست (@JanDost99) on X|websîte=Twitter|url=https://x.com/JanDost99/status/1907902342587920623|tarîx=2025-04-03|jêgirtin=Bi '''muzîsyenê''' mezin Dilşad Seîd re.}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Formên alternatîv ====
* {{g|ku|muzisyen}}
* {{g|ku|mûzisyen}}
* {{g|ku|mûzîsyen}}
==== Jê ====
* {{g|ku|muzîsyenî}}
* {{g|ku|muzîsyenîtî}}
* {{g|ku|muzîsyentî}}
==== Têkildar ====
* {{g|ku|bilûrvan}}
* {{g|ku|deholvan}}
* {{g|ku|sazvan}}
* {{g|ku|tembûrvan}}
* {{g|ku|zirrnevan}}
==== Etîmolojî ====
Ji {{ji|fr|ku|musicien}}.
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|musikant}}, {{W-|af|musikus}}, {{W+|af|musici|p}}
* {{Z|sq}}: {{W+|sq|muzikant|n}}
* {{Z|de}}: {{W+|de|Musiker|n}}, {{W+|de|Musikant|n}}, {{W+|de|Tonkünstler|n}}, {{W+|de|Musikus|n}}
* {{Z|ast}}: {{W-|ast|músicu|n}}
* {{Z|az}}: {{W-|az|musiqiçi}}
* {{Z|eu}}: {{W-|eu|musikari}}
* {{Z|be}}: {{W-|be|музы́ка|n|sc=Cyrl}}, {{W-|be|музыка́нт|n|sc=Cyrl}}
* {{Z|bn}}: {{W+|bn|বাদক}}
* {{Z|br}}: {{W+|br|soner|n}}, {{W-|br|sonour|n}}, {{W-|br|muzisian|n}}, {{W-|br|muziker|n}}
* {{Z|bg}}: {{W-|bg|музика́нт|n}}
* {{Z|my}}: {{W+|my|ဂီတသမား|sc=Mymr}}
* {{Z|ce}}: {{W-|ce|музыкант}}
* {{Z|cs}}: {{W+|cs|hudebník|n}}, {{W+|cs|muzikant|n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W+|cmn|音樂家|tr=yīnyuèjiā}}
* {{Z|ckt}}: {{W-|ckt|эʼйӈэнэӈэткульын|tr=ėʼjṇėnėṇėtkulʹyn}}
* {{Z|da}}: {{W+|da|musiker|g}}
* {{Z|id}}: {{W+|id|musikus}}, {{W+|id|pemusik}}
* {{Z|ar}}: {{W+|ar|مُوسِيقِيّ|n}}
* {{Z|hy}}: {{W+|hy|երաժիշտ}}
* {{Z|eo}}: {{W+|eo|muzikisto}}
* {{Z|et}}: {{W+|et|muusik}}
* {{Z|fa}}: {{W+|fa|موزیسین|tr=muzisian}}, {{W-|fa|موزیکان|tr=muzikân}}, {{W+|fa|موسیقیدان|tr=musiqidân}}
* {{Z|fi}}: {{W+|fi|muusikko}}
* {{Z|fr}}: {{W+|fr|musicien|n}}, {{W+|fr|musicienne|m}}
* {{Z|fy}}: {{W-|fy|muzikus|n|m}}, {{W-|fy|muzikant|n}}, {{W-|fy|muzikante|m}}, {{W-|fy|muzykman|n}}
* {{Z|gd}}: {{W-|gd|ceòladair|n}}, {{W-|gd|neach-ciùil|n}}
* {{Z|gl}}: {{W+|gl|músico|n}}, {{W+|gl|música|m}}
* {{Z|ka}}: {{W-|ka|მუსიკოსი}}
* {{Z|hi}}: {{W+|hi|संगीतकार|n}}
* {{Z|nl}}: {{W+|nl|muzikant|n}}, {{W+|nl|muzikante|m}}, {{W+|nl|musicus|n|m}}
* {{Z|he}}: {{W-|he|מוּזִיקַאי|tr=muzikai}}
* {{Z|ang}}: {{W-|ang|dreāmere|n}}
* {{Z|enm}}: {{W-|enm|musicien}}, {{W-|enm|musiker}}, {{W-|enm|synger}}
* {{Z|en}}: {{W+|en|musician}}
* {{Z|ga}}: {{W-|ga|ceoltóir|n}}
* {{Z|it}}: {{W+|it|musicista|n|m}}, {{W+|it|musico|n}}
* {{Z|is}}: {{W+|is|tónlistarmaður|n}}
* {{Z|ja}}: {{W+|ja|音楽家|tr=おんがくか, ongakuka}}
* {{Z|jv}}: {{W-|jv|niyógo}}
* {{Z|kl}}: {{W-|kl|nipilersortartoq}}
* {{Z|xal}}: {{W-|xal|көгҗмч|sc=Cyrl}}
* {{Z|ca}}: {{W+|ca|músic|n}}
* {{Z|ky}}: {{W+|ky|музыкант|sc=Cyrl}}
* {{Z|ko}}: {{W+|ko|음악가}}
* {{Z|kw}}: {{W-|kw|ilewydh|n}}, {{W-|kw|ilewydhes|m}}
* {{Z|co}}: {{W-|co|musicante|n|m}}
* {{Z|la}}: {{W-|la|mūsicus|n}}, {{W-|la|mūsica|m}}
* {{Z|lv}}: {{W-|lv|mūziķis|n}}
* {{Z|lo}}: {{W+|lo|ນັກດົນຕີ}}
* {{Z|lij}}: {{W-|lij|mûxicante|n}}
* {{Z|lt}}: {{W-|lt|muzikantas|n}}, {{W-|lt|muzikas|n}}
* {{Z|lmo}}: {{W-|lmo|musicista|n}}
* {{Z|lb}}: {{W-|lb|Museker|n}}, {{W-|lb|Musekerin|m}}
* {{Z|mk}}: {{W-|mk|му́зичар|n}}
* {{Z|ms}}: {{W-|ms|pemuzik}}
* {{Z|mi}}: {{W-|mi|kaiwhakatangi}}, {{W+|mi|kai}}, {{W-|mi|kaipuoro}}, {{W-|mi|kiripuoro}}
* {{Z|hu}}: {{W+|hu|zenész}}, {{W+|hu|muzsikus}}
* {{Z|mn}}: {{W+|mn|хөгжимчин|sc=Cyrl}}
* {{Z|nhg}}: {{W-|nhg|mursi̱co}}
* {{Z|nrf}}: {{W-|nrf|musicien|n}}
* {{Z|no}}:
*: {{Z|nb}}: {{W-|nb|musiker|n}}
*: {{Z|nn}}: {{W-|nn|musikar|n}}
* {{Z|uz}}: {{W+|uz|musiqachi}}, {{W+|uz|muzikant}}
* {{Z|pi}}: {{W-|pi|vādaka|n}}
* {{Z|pms}}: {{W-|pms|musicista|n}}
* {{Z|pl}}: {{W+|pl|muzyk|m-pr}}, {{W+|pl|muzyczka|m}}
* {{Z|pt}}: {{W+|pt|músico|n}}
* {{Z|kk}}: {{W-|kk|музыкант|sc=Cyrl}}
* {{Z|rgn}}: {{W-|rgn|mușicânt|n}}
* {{Z|ro}}: {{W+|ro|muzician|n}}, {{W+|ro|muziciană|m}}
* {{Z|ru}}: {{W-|ru|музыка́нт|n}}, {{W-|ru|музыка́нтка|m|sc=Cyrl}}
* {{Z|sa}}: {{W+|sa|वादक|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|музичар|n|sc=Cyrl}}
*: Latînî: {{W+|sh|muzičar|n}}, {{W+|sh|glazbenik|n}}
* {{Z|sco}}: {{W-|sco|muisicker}}, {{W-|sco|muisician}}
* {{Z|sk}}: {{W-|sk|hudobník|n}}, {{W-|sk|muzikant|n}}
* {{Z|sl}}: {{W+|sl|glasbenik|n}}, {{W-|sl|glasbenica|m}}
* {{Z|st}}: {{W-|st|sebini}}
* {{Z|es}}: {{W+|es|músico|n}}
* {{Z|sw}}: {{W+|sw|mwimbaji}}, {{W+|sw|mwanamuziki}}
* {{Z|sv}}: {{W+|sv|musikant|g}}, {{W+|sv|musiker|g}}
* {{Z|tg}}: {{W-|tg|мусиқачӣ}}
* {{Z|tl}}: {{W-|tl|musiko}}
* {{Z|rif}}: {{W-|rif|amedyaz|n}}
* {{Z|th}}: {{W-|th|นักดนตรี}}
* {{Z|tr}}: {{W+|tr|müzisyen}}
* {{Z|tk}}: {{W-|tk|sazandar}}, {{W-|tk|sazanda}}
* {{Z|ur}}: {{W-|ur|موسیقار|n|tr=musiqār|sc=ur-Arab}}
* {{Z|uk}}: {{W-|uk|музика́нт|n}}
* {{Z|vi}}: {{W+|vi|nhạc sĩ}}
* {{Z|vo}}: {{W+|vo|musigan}}
* {{Z|cy}}: {{W+|cy|cerddwr|n}}
* {{Z|km}}: {{W-|km|អ្នកភ្លេង}}, {{W+|km|វាទកៈ}}
* {{Z|yi}}: {{W-|yi|מוזיקער|n}}
* {{Z|el}}: {{W+|el|μουσικός|n|m}}
*: Kevn: {{W-|grc|μουσικός|n}}
{{werger-bin}}
:{{werger çavkanî|en|musician|beş=Ev qismê Wergerê|oldid=75678903}}
{{mijar|ku|muzîsyen|kes}}
ldg3wiqumxnvj6b42zcncb7xw3gpge6
kesme
0
42503
6334232
6309991
2026-08-16T21:00:50Z
Zerwanda
12183
6334232
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[kade]], [[kulî]], [[kêk]]
#* {{bêmînak|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê|guherto=hemû}}
==== Etîmolojî ====
Hevreh bi {{hv|fa|کسمه}}, {{hv|az|kəsmə}}, ji zimanekî tirkîkî {{q|tr|kesme|w=birrîn}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W|en|cake}}, {{W|en|pie}}, {{W|en|tart}}
* {{Z|tr}}: {{W|tr|köfter}}, {{W|tr|tart}}
{{werger-bin}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[birîn]], [[jêkirin]]
#* {{bêmînak|tr}}
==== Etîmolojî ====
[[kesmek]]
fad4fyzpo5z72cbgthe763c9xvak99d
6334235
6334232
2026-08-16T21:01:24Z
Zerwanda
12183
/* Etîmolojî */
6334235
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[kade]], [[kulî]], [[kêk]]
#* {{bêmînak|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê|guherto=hemû}}
==== Etîmolojî ====
Hevreh bi {{hv|ckb|کاسمە}}, {{q|ckb|کەسمە}}, {{hv|fa|کسمه}}, {{hv|az|kəsmə}}, ji zimanekî tirkîkî {{q|tr|kesme|w=birrîn}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W|en|cake}}, {{W|en|pie}}, {{W|en|tart}}
* {{Z|tr}}: {{W|tr|köfter}}, {{W|tr|tart}}
{{werger-bin}}
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[birîn]], [[jêkirin]]
#* {{bêmînak|tr}}
==== Etîmolojî ====
[[kesmek]]
e0hdue4xstvr3af1tay50g7kk0m9t2l
perxaş
0
45167
6334199
6314376
2026-08-16T19:51:38Z
Zerwanda
12183
6334199
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[şer]], [[ceng]], [[pevçûn]]
#* {{bêmînak|ku}}
==== Ji wêjeyê ====
''Mîr go: Qe didim eya bed exter <br>Tacdîn û Memê bi textê Qeyser?<br>Roja we ku bûye ceng û perxaş<br>Tacdîn û Memê du sed [[qizilbaş|Qizilbaş]]<br>[[bilcumle|Bilcumle]] bi [[zirh]] û [[zend]] û [[binber]]<br>[[înan|Înan]] ji me ra bi [[şûr|şîr]] û [[şeşper]].''
(Ehmedê Xanî: ''Mem û Zîn'', 1695)
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
====Etîmolojî====
Hevreh bi {{hv|tr|pargaç}}, {{hv|hu|pogácsa}}, ji zimanekî slavî; bide ber {{hv|sh|pogača}}, {{hv|bg|погача}}, {{hv|mk|погача|w=nanê şkeva}}, ji {{hv|la|focācius|w=ya ocaxê}}, ji {{q|la|focus|w=ocax}}, çumkî li wextên pêş-nûjen li ser xweliya tifikê dihatin pêjan. {{cotreh|ku|poxaça}}.
6v086sukdv23odhuhn323rxt0pfftfg
6334200
6334199
2026-08-16T19:52:21Z
Zerwanda
12183
6334200
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[şer]], [[ceng]], [[pevçûn]]
#* {{bêmînak|ku}}
==== Ji wêjeyê ====
''Mîr go: Qe didim eya bed exter <br>Tacdîn û Memê bi textê Qeyser?<br>Roja we ku bûye ceng û perxaş<br>Tacdîn û Memê du sed [[qizilbaş|Qizilbaş]]<br>[[bilcumle|Bilcumle]] bi [[zirh]] û [[zend]] û [[binber]]<br>[[înan|Înan]] ji me ra bi [[şûr|şîr]] û [[şeşper]].''
(Ehmedê Xanî: ''Mem û Zîn'', 1695)
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
s24k1f86vjr3bkkxgnv2r4wmwnmua11
Îsmaîl
0
46287
6334158
6321601
2026-08-16T17:44:16Z
Wikihez
7344
/* Formên alternatîv */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334158
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA|îsma'îl}}, Îsma'îl, ئیسماعیل
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[nav|Navekî]] {{navkes|ku|law}}
# {{f|ku|dîn}} [[kur]]ê [[mezin]] yê [[Îbrahîm]]-[[pêxember]]î
#* {{bênimûne|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|nêr}}
==== Formên alternatîv ====
* [[Ismaîl]]
* [[Simaîl]]
* [[Simo]]
* [[Simko]]
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W-|de|Ismael|n}}
* {{Z|aii}}: {{W-|aii|ܐܝܼܫܡܲܥܹܝܠ|n}}
* {{Z|bn}}: {{W-|bn|ইসমাঈল|tr=ismail}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W-|cmn|以實瑪利|tr=Yǐshímǎlì}}, {{W-|cmn|依市瑪耳|tr=Yīshìmǎ'ěr}}
* {{Z|ar}}: {{W-|ar|إِسْمَاعِيل|n}}
* {{Z|ee}}: {{W-|ee|Ismael|n}}
* {{Z|fa}}: {{W+|fa|اسماعیل|tr=esmâ'il}}
* {{Z|fi}}: {{W-|fi|Ismael}}
* {{Z|fr}}: {{W+|fr|Ismaël|n}}
* {{Z|ha}}: {{W-|ha|Isma'ilu}}
* {{Z|hi}}: {{W+|hi|इसमाईल|n}}
* {{Z|nl}}: {{W-|nl|Ismaël|n}}
* {{Z|he}}: {{W+|he|יִשְׁמָעֵאל|n|tr=yishma'él|sc=Hebr}}
* {{Z|en}}: {{W+|en|Ishmael}}
* {{Z|ga}}: {{W-|ga|Ísmeáéil|n}}
* {{Z|it}}: {{W-|it|Ismaele}}
* {{Z|ja}}: {{W-|ja|イシュマエル|tr=Ishumaeru|sc=Jpan}}
* {{Z|ca}}: {{W-|ca|Ismael}}
* {{Z|swb}}: {{W-|swb|Ismaila}}
* {{Z|ko}}: {{W-|ko|^이스마엘}}
* {{Z|la}}: {{W-|la|Ismahel|n}}, {{W-|la|Ismael|n}}
* {{Z|mk}}: {{W-|mk|Исмаил|n}}
* {{Z|ms}}: {{W-|ms|Ismail}}
* {{Z|hu}}: {{W+|hu|Izmael}}
* {{Z|pt}}: {{W-|pt|Ismael|n}}
* {{Z|kk}}: {{W-|kk|Ысмайыл}}
* {{Z|rhg}}: {{W-|rhg|Ismail}}
* {{Z|ru}}: {{W+|ru|Измаи́л|n}}, {{W+|ru|Исмаи́л|n}}
* {{Z|so}}: {{W-|so|Ismaaciil}}
* {{Z|es}}: {{W+|es|Ismael|n}}
* {{Z|sv}}: {{W-|sv|Ismael}}
* {{Z|tg}}: {{W-|tg|Исмоил}}
* {{Z|tl}}: {{W-|tl|Ismael}}
* {{Z|ta}}: {{W+|ta|இஸ்மாயீல்}}, {{W-|ta|இஸ்மவேல்}}
* {{Z|tr}}: {{W+|tr|İsmail}}
* {{Z|el}}: {{W+|el|Ισμαήλ|n}}
*: Kevn: {{W-|grc|Ἰσμαήλ|n}}
{{werger-bin}}
:{{werger çavkanî|en|Ishmael|beş=Ev qismê Wergerê|oldid=76287204}}
pxtwm06wzqf70h0nq86uo0s61w0edyf
qerqeşûn
0
46545
6334172
6326658
2026-08-16T18:03:32Z
Wikihez
7344
[[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin, [[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6334172
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[maden|Maden]]ek [[sengîn]] e û nîşana wî [[Pb]] ye.
#: {{hevmane|ku|zirêç|sirb|risas|qurşin}}
#* Revdegurga ku cara pêşî sala 1983yan hatiye weşandin û sala 1990î bûye fîlm, alegoriyeke mînîmalîst e ku bi hemû sadetiya xwe nebaniya li civakê digire navenda xwe û di ser monologan re me nêzî têvelî û '''qerqeşûna''' ruhî dike.<ref>https://wer-ger.com/revdegurg/</ref>
==== Formên alternatîv ====
* {{g|ku|xerxeeşûn}}
==== Jê ====
* {{g|ku|qerqeşûnî}}
==== Etîmolojî ====
Ji {{ji|trk}}, herwiha bide ber {{hv|az|qurğuşun}}. Hevreha {{g|ku|qurşin}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|قورقوشم|tr=[[qurquşm]]}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|lood}}
* {{Z|sq}}: {{W+|sq|plumb}}
* {{Z|de}}: {{W+|de|Blei|n}}
* {{Z|eu}}: {{W-|eu|beruna}}
* {{Z|be}}: {{W-|be|цвінец|tr=tsvinéts}}
* {{Z|br}}: {{W+|br|plom|m}}, plomoù {{p}}
* {{Z|bg}}: {{W-|bg|олово|tr=olovo}}
* {{Z|cs}}: {{W+|cs|olovo}}
* {{Z|zh}}: {{W+|zh|鉛}}, [[铅]] (qiān)
* {{Z|da}}: {{W+|da|bly}}
* {{Z|id}}: {{W+|id|timbal}}
* {{Z|ar}}: {{W-|ar|رصاص}} {{unicode|(raʂá: ʂ)}}
* {{Z|hy}}: {{W-|hy|կապար|tr=kapar}}
* {{Z|eo}}: {{W+|eo|plumbo}}
* {{Z|et}}: {{W+|et|plii}}
* {{Z|fa}}: {{W+|fa|سرب|tr=sorb}}, {{W-|fa|آشفته بازار}}, {{W+|fa|پولاد}}
* {{Z|fo}}: {{W-|fo|blýggj}}
* {{Z|fi}}: {{W+|fi|lyijy}}
* {{Z|fr}}: {{W+|fr|plomb|m}}
* {{Z|fur}}: {{W-|fur|plomp}}
* {{Z|fy}}: {{W-|fy|lead|n}}
* {{Z|gd}}: {{W-|gd|luaidh}}
* {{Z|el}}: {{W-|gd|μόλυβδος}}
* {{Z|gl}}: {{W+|gl|chumbo}}
* {{Z|ka}}: {{W-|ka|ტყვია|tr=tqvia}}
* {{Z|nl}}: {{W+|nl|lood|n}}
* {{Z|he}}: {{W-|he|עופרת|tr=‘oferet}}
* {{Z|en}}: {{W-|en|lead}}, {{W+|en|plumbo-}}
* {{Z|ga}}: {{W-|ga|luaidhe}}
* {{Z|it}}: {{W+|it|piombo|m}}
* {{Z|is}}: {{W+|is|blý}}
* {{Z|ja}}: {{W+|ja|鉛|tr=[[なまり]], namarí}}
* {{Z|ca}}: {{W+|ca|plom|m}}
* {{Z|ko}}: {{W-|ko|납|tr=nap}}, [[연]] (鉛, {{W-|ko|yeon)}}
* {{Z|kw}}: {{W-|kw|plom}}
* {{Z|la}}: {{W+|la|plumbum}}
* {{Z|lt}}: {{W+|lt|svins}}, {{W+|lt|švinas|m}}
* {{Z|lb}}: {{W-|lb|bläi}}
* {{Z|mk}}: {{W-|mk|олово|tr=olovo}}
* {{Z|ml}}: {{W-|ml|കറുത്തീയം|tr=kaRutheeyam}}
* {{Z|ms}}: {{W-|ms|plumbum}}
* {{Z|mt}}: {{W-|mt|ċomb}}
* {{Z|gv}}: {{W-|gv|leoaie}}
* {{Z|mi}}: {{W-|mi|matā}}
* {{Z|hu}}: {{W+|hu|ólom}}
* {{Z|mn}}: {{W-|mn|хар|tr=khar}}
* {{Z|no}}: {{W+|no|bly}}
* {{Z|uz}}: {{W-|uz|кўогъшсин|tr=kŭog”şsin}}
* {{Z|pl}}: {{W+|pl|ołów|m}}
* {{Z|pt}}: {{W+|pt|chumbo|m}}
* {{Z|kk}}: {{W-|kk|къогъасын|tr=qog”asın}}
* {{Z|ro}}: {{W+|ro|plumb|n}}
* {{Z|ru}}: {{W-|ru|свинец|tr=svinéts}}, [[плюмбум]] (pljúmbum)
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W-|sh|олово|tr=[[olovo]]}}
* {{Z|sk}}: {{W+|sk|olovo}}
* {{Z|sl}}: {{W+|sl|svinec|m}}
* {{Z|es}}: {{W+|es|plomo|m}}
* {{Z|sv}}: {{W+|sv|bly}}
* {{Z|tg}}: {{W-|tg|surb}}
* {{Z|ta}}: {{W-|ta|ஈயம்|tr=īyam}}
* {{Z|th}}: {{W-|th|ตะกั้ว|tr=tàgûa}}
* {{Z|te}}: {{W-|te|సీసం|tr=seesam}}
* {{Z|tr}}: {{W+|tr|kurşun}}, {{W+|tr|kargaşa}}, {{W+|tr|karışıklık}}
* {{Z|uk}}: {{W-|uk|свинець|tr=svynéts’}}
* {{Z|vi}}: {{W+|vi|chì}}
* {{Z|cy}}: {{W+|cy|plwm}}
* {{Z|yi}}: {{W-|yi|בליי|tr=blei}}
{{werger-bin}}
lj4i2xrn46ii5mfp152v4t417yvrfkw
6334174
6334172
2026-08-16T18:05:12Z
Wikihez
7344
6334174
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[maden|Maden]]ek [[sengîn]] e û nîşana wî [[Pb]] ye.
#: {{hevmane|ku|zirêç|sirb|risas|qurşin}}
#* {{jêder-websîte|ku|jêgirtin=Revdegurga ku cara pêşî sala 1983yan hatiye weşandin û sala 1990î bûye fîlm, alegoriyeke mînîmalîst e ku bi hemû sadetiya xwe nebaniya li civakê digire navenda xwe û di ser monologan re me nêzî têvelî û '''qerqeşûna''' ruhî dike.|sernav=Revdegurg|websîte=wer-ger.com|url=https://wer-ger.com/revdegurg/|tarîx=2026-02-08}}
==== Formên alternatîv ====
* {{g|ku|xerxeeşûn}}
==== Jê ====
* {{g|ku|qerqeşûnî}}
==== Etîmolojî ====
Ji {{ji|trk}}, herwiha bide ber {{hv|az|qurğuşun}}. Hevreha {{g|ku|qurşin}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|قورقوشم|tr=[[qurquşm]]}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|lood}}
* {{Z|sq}}: {{W+|sq|plumb}}
* {{Z|de}}: {{W+|de|Blei|n}}
* {{Z|eu}}: {{W-|eu|beruna}}
* {{Z|be}}: {{W-|be|цвінец|tr=tsvinéts}}
* {{Z|br}}: {{W+|br|plom|m}}, plomoù {{p}}
* {{Z|bg}}: {{W-|bg|олово|tr=olovo}}
* {{Z|cs}}: {{W+|cs|olovo}}
* {{Z|zh}}: {{W+|zh|鉛}}, [[铅]] (qiān)
* {{Z|da}}: {{W+|da|bly}}
* {{Z|id}}: {{W+|id|timbal}}
* {{Z|ar}}: {{W-|ar|رصاص}} {{unicode|(raʂá: ʂ)}}
* {{Z|hy}}: {{W-|hy|կապար|tr=kapar}}
* {{Z|eo}}: {{W+|eo|plumbo}}
* {{Z|et}}: {{W+|et|plii}}
* {{Z|fa}}: {{W+|fa|سرب|tr=sorb}}, {{W-|fa|آشفته بازار}}, {{W+|fa|پولاد}}
* {{Z|fo}}: {{W-|fo|blýggj}}
* {{Z|fi}}: {{W+|fi|lyijy}}
* {{Z|fr}}: {{W+|fr|plomb|m}}
* {{Z|fur}}: {{W-|fur|plomp}}
* {{Z|fy}}: {{W-|fy|lead|n}}
* {{Z|gd}}: {{W-|gd|luaidh}}
* {{Z|el}}: {{W-|gd|μόλυβδος}}
* {{Z|gl}}: {{W+|gl|chumbo}}
* {{Z|ka}}: {{W-|ka|ტყვია|tr=tqvia}}
* {{Z|nl}}: {{W+|nl|lood|n}}
* {{Z|he}}: {{W-|he|עופרת|tr=‘oferet}}
* {{Z|en}}: {{W-|en|lead}}, {{W+|en|plumbo-}}
* {{Z|ga}}: {{W-|ga|luaidhe}}
* {{Z|it}}: {{W+|it|piombo|m}}
* {{Z|is}}: {{W+|is|blý}}
* {{Z|ja}}: {{W+|ja|鉛|tr=[[なまり]], namarí}}
* {{Z|ca}}: {{W+|ca|plom|m}}
* {{Z|ko}}: {{W-|ko|납|tr=nap}}, [[연]] (鉛, {{W-|ko|yeon)}}
* {{Z|kw}}: {{W-|kw|plom}}
* {{Z|la}}: {{W+|la|plumbum}}
* {{Z|lt}}: {{W+|lt|svins}}, {{W+|lt|švinas|m}}
* {{Z|lb}}: {{W-|lb|bläi}}
* {{Z|mk}}: {{W-|mk|олово|tr=olovo}}
* {{Z|ml}}: {{W-|ml|കറുത്തീയം|tr=kaRutheeyam}}
* {{Z|ms}}: {{W-|ms|plumbum}}
* {{Z|mt}}: {{W-|mt|ċomb}}
* {{Z|gv}}: {{W-|gv|leoaie}}
* {{Z|mi}}: {{W-|mi|matā}}
* {{Z|hu}}: {{W+|hu|ólom}}
* {{Z|mn}}: {{W-|mn|хар|tr=khar}}
* {{Z|no}}: {{W+|no|bly}}
* {{Z|uz}}: {{W-|uz|кўогъшсин|tr=kŭog”şsin}}
* {{Z|pl}}: {{W+|pl|ołów|m}}
* {{Z|pt}}: {{W+|pt|chumbo|m}}
* {{Z|kk}}: {{W-|kk|къогъасын|tr=qog”asın}}
* {{Z|ro}}: {{W+|ro|plumb|n}}
* {{Z|ru}}: {{W-|ru|свинец|tr=svinéts}}, [[плюмбум]] (pljúmbum)
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W-|sh|олово|tr=[[olovo]]}}
* {{Z|sk}}: {{W+|sk|olovo}}
* {{Z|sl}}: {{W+|sl|svinec|m}}
* {{Z|es}}: {{W+|es|plomo|m}}
* {{Z|sv}}: {{W+|sv|bly}}
* {{Z|tg}}: {{W-|tg|surb}}
* {{Z|ta}}: {{W-|ta|ஈயம்|tr=īyam}}
* {{Z|th}}: {{W-|th|ตะกั้ว|tr=tàgûa}}
* {{Z|te}}: {{W-|te|సీసం|tr=seesam}}
* {{Z|tr}}: {{W+|tr|kurşun}}, {{W+|tr|kargaşa}}, {{W+|tr|karışıklık}}
* {{Z|uk}}: {{W-|uk|свинець|tr=svynéts’}}
* {{Z|vi}}: {{W+|vi|chì}}
* {{Z|cy}}: {{W+|cy|plwm}}
* {{Z|yi}}: {{W-|yi|בליי|tr=blei}}
{{werger-bin}}
h4q1vlhr7gbxg4lvughiju9f7iag6aq
6334193
6334174
2026-08-16T19:12:10Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir.)
6334193
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[maden|Madenek]] [[sengîn]] e û nîşana wî [[Pb]] ye.
#: {{hevmane|ku|zirêç|sirb|risas|qurşin}}
#* {{jêder-websîte|ku|jêgirtin=Revdegurga ku cara pêşî sala 1983yan hatiye weşandin û sala 1990î bûye fîlm, alegoriyeke mînîmalîst e ku bi hemû sadetiya xwe nebaniya li civakê digire navenda xwe û di ser monologan re me nêzî têvelî û '''qerqeşûna''' ruhî dike.|sernav=Revdegurg|websîte=wer-ger.com|url=https://wer-ger.com/revdegurg/|tarîx=2026-02-08}}
==== Formên alternatîv ====
* {{g|ku|xerxeeşûn}}
==== Jê ====
* {{g|ku|qerqeşûnî}}
==== Etîmolojî ====
Ji {{ji|trk}}, herwiha bide ber {{hv|az|qurğuşun}}. Hevreha {{g|ku|qurşin}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|قورقوشم|tr=[[qurquşm]]}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|lood}}
* {{Z|sq}}: {{W+|sq|plumb}}
* {{Z|de}}: {{W+|de|Blei|n}}
* {{Z|eu}}: {{W-|eu|beruna}}
* {{Z|be}}: {{W-|be|цвінец|tr=tsvinéts}}
* {{Z|br}}: {{W+|br|plom|m}}, plomoù {{p}}
* {{Z|bg}}: {{W-|bg|олово|tr=olovo}}
* {{Z|cs}}: {{W+|cs|olovo}}
* {{Z|zh}}: {{W+|zh|鉛}}, [[铅]] (qiān)
* {{Z|da}}: {{W+|da|bly}}
* {{Z|id}}: {{W+|id|timbal}}
* {{Z|ar}}: {{W-|ar|رصاص}} {{unicode|(raʂá: ʂ)}}
* {{Z|hy}}: {{W-|hy|կապար|tr=kapar}}
* {{Z|eo}}: {{W+|eo|plumbo}}
* {{Z|et}}: {{W+|et|plii}}
* {{Z|fa}}: {{W+|fa|سرب|tr=sorb}}, {{W-|fa|آشفته بازار}}, {{W+|fa|پولاد}}
* {{Z|fo}}: {{W-|fo|blýggj}}
* {{Z|fi}}: {{W+|fi|lyijy}}
* {{Z|fr}}: {{W+|fr|plomb|m}}
* {{Z|fur}}: {{W-|fur|plomp}}
* {{Z|fy}}: {{W-|fy|lead|n}}
* {{Z|gd}}: {{W-|gd|luaidh}}
* {{Z|el}}: {{W-|gd|μόλυβδος}}
* {{Z|gl}}: {{W+|gl|chumbo}}
* {{Z|ka}}: {{W-|ka|ტყვია|tr=tqvia}}
* {{Z|nl}}: {{W+|nl|lood|n}}
* {{Z|he}}: {{W-|he|עופרת|tr=‘oferet}}
* {{Z|en}}: {{W-|en|lead}}, {{W+|en|plumbo-}}
* {{Z|ga}}: {{W-|ga|luaidhe}}
* {{Z|it}}: {{W+|it|piombo|m}}
* {{Z|is}}: {{W+|is|blý}}
* {{Z|ja}}: {{W+|ja|鉛|tr=[[なまり]], namarí}}
* {{Z|ca}}: {{W+|ca|plom|m}}
* {{Z|ko}}: {{W-|ko|납|tr=nap}}, [[연]] (鉛, {{W-|ko|yeon)}}
* {{Z|kw}}: {{W-|kw|plom}}
* {{Z|la}}: {{W+|la|plumbum}}
* {{Z|lt}}: {{W+|lt|svins}}, {{W+|lt|švinas|m}}
* {{Z|lb}}: {{W-|lb|bläi}}
* {{Z|mk}}: {{W-|mk|олово|tr=olovo}}
* {{Z|ml}}: {{W-|ml|കറുത്തീയം|tr=kaRutheeyam}}
* {{Z|ms}}: {{W-|ms|plumbum}}
* {{Z|mt}}: {{W-|mt|ċomb}}
* {{Z|gv}}: {{W-|gv|leoaie}}
* {{Z|mi}}: {{W-|mi|matā}}
* {{Z|hu}}: {{W+|hu|ólom}}
* {{Z|mn}}: {{W-|mn|хар|tr=khar}}
* {{Z|no}}: {{W+|no|bly}}
* {{Z|uz}}: {{W-|uz|кўогъшсин|tr=kŭog”şsin}}
* {{Z|pl}}: {{W+|pl|ołów|m}}
* {{Z|pt}}: {{W+|pt|chumbo|m}}
* {{Z|kk}}: {{W-|kk|къогъасын|tr=qog”asın}}
* {{Z|ro}}: {{W+|ro|plumb|n}}
* {{Z|ru}}: {{W-|ru|свинец|tr=svinéts}}, [[плюмбум]] (pljúmbum)
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W+|sh|olovo}}
* {{Z|sh}}: {{W-|sh|олово|tr=[[olovo]]}}
* {{Z|sk}}: {{W+|sk|olovo}}
* {{Z|sl}}: {{W+|sl|svinec|m}}
* {{Z|es}}: {{W+|es|plomo|m}}
* {{Z|sv}}: {{W+|sv|bly}}
* {{Z|tg}}: {{W-|tg|surb}}
* {{Z|ta}}: {{W-|ta|ஈயம்|tr=īyam}}
* {{Z|th}}: {{W-|th|ตะกั้ว|tr=tàgûa}}
* {{Z|te}}: {{W-|te|సీసం|tr=seesam}}
* {{Z|tr}}: {{W+|tr|kurşun}}, {{W+|tr|kargaşa}}, {{W+|tr|karışıklık}}
* {{Z|uk}}: {{W-|uk|свинець|tr=svynéts’}}
* {{Z|vi}}: {{W+|vi|chì}}
* {{Z|cy}}: {{W+|cy|plwm}}
* {{Z|yi}}: {{W-|yi|בליי|tr=blei}}
{{werger-bin}}
q0ecb5u4ydc3liiqk2sw9k2ut32waay
veniştin
0
48237
6334093
6334073
2026-08-16T13:26:08Z
Wikihez
7344
/* Lêker */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334093
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|ve|niş|tin}}
=== Lêker ===
{{lêker|ku|c=x-ngh}}
# Bi ser derekê ve vegirtin û li ser danîn, [[li ser rûniştin]].
#* {{jêder|ku|jêgirtin=Min dizanibû ku ew dê bi kêfxweşî were nik min '''venişe''' û ava xwe vexwe, qûta xwe bixwe.|{{Jêgirtin/Mehmed Uzun/Mirina Kalekî Rind/1987}}}}
#* {{jêder-kitêb|ku|nivîskar=Mehmed Uzun|sernav=Siya Evînê|weşanxane=İthaki|cih=İstanbul|sal=1989|sala-weşandinê=2005|isbn=975-273-098-1|jêgirtin=Bellê, ew jin bû ku wan ê silav lê bikirina û li ser milên wê '''veniştina'''.}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|landen|guman=erê}}, {{W+|de|setzen|guman=erê}}, {{W-|de|sich niederlassen|guman=erê}}, {{W+|de|Abscheidung|guman=erê}}, {{W-|de|Ausfällung|guman=erê}}, {{W+|de|Sitz|guman=erê}}
* {{Z|fa}}: {{W+|fa|فرود آمدن|guman=erê}}, {{W+|fa|قرار گرفتن|guman=erê}}
* {{Z|en}}: {{W+|en|berth|guman=erê}}, {{W+|en|coming down|guman=erê}}
* {{Z|tr}}: {{W+|tr|konmak|guman=erê}}, {{W-|tr|menzil tutmak|guman=erê}}, {{W+|tr|tüneklemek|guman=erê}}, {{W+|tr|tünemek|guman=erê}}, {{W+|tr|konma|guman=erê}}, {{W+|tr|konuş|guman=erê}}, {{W+|tr|tünekleme|guman=erê}}, {{W+|tr|tüneme|guman=erê}}
{{werger-bin}}
ji16az6q0b3g7xr7b5eg9ap7rz7mmir
6334094
6334093
2026-08-16T13:48:39Z
Wikihez
7344
/* Lêker */
6334094
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|ve|niş|tin}}
=== Lêker ===
{{lêker|ku|c=x-ngh}}
# Bi ser derekê ve vegirtin û li ser danîn, [[li ser rûniştin]].
#* {{jêder|ku|jêgirtin=Min dizanibû ku ew dê bi kêfxweşî were nik min '''venişe''' û ava xwe vexwe, qûta xwe bixwe.|{{Jêgirtin/Mehmed Uzun/Mirina Kalekî Rind/1987}}}}
#* {{jêder-kitêb|ku|nivîskar=Mehmed Uzun|sernav=Siya Evînê|weşanxane2=İthaki|cih2=İstanbul|sal=1989|sal2=2005|isbn2=975-273-098-1|jêgirtin=Bellê, ew jin bû ku wan ê silav lê bikirina û li ser milên wê '''veniştina'''.}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|landen|guman=erê}}, {{W+|de|setzen|guman=erê}}, {{W-|de|sich niederlassen|guman=erê}}, {{W+|de|Abscheidung|guman=erê}}, {{W-|de|Ausfällung|guman=erê}}, {{W+|de|Sitz|guman=erê}}
* {{Z|fa}}: {{W+|fa|فرود آمدن|guman=erê}}, {{W+|fa|قرار گرفتن|guman=erê}}
* {{Z|en}}: {{W+|en|berth|guman=erê}}, {{W+|en|coming down|guman=erê}}
* {{Z|tr}}: {{W+|tr|konmak|guman=erê}}, {{W-|tr|menzil tutmak|guman=erê}}, {{W+|tr|tüneklemek|guman=erê}}, {{W+|tr|tünemek|guman=erê}}, {{W+|tr|konma|guman=erê}}, {{W+|tr|konuş|guman=erê}}, {{W+|tr|tünekleme|guman=erê}}, {{W+|tr|tüneme|guman=erê}}
{{werger-bin}}
3kwtrz5o2jqjz2jejk1xz2anunkmr1e
6334095
6334094
2026-08-16T13:52:50Z
Wikihez
7344
/* Lêker */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334095
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|ve|niş|tin}}
=== Lêker ===
{{lêker|ku|c=x-ngh}}
# Bi ser derekê ve vegirtin û li ser danîn, [[li ser rûniştin]].
#* {{jêder|ku|jêgirtin=Min dizanibû ku ew dê bi kêfxweşî were nik min '''venişe''' û ava xwe vexwe, qûta xwe bixwe.|{{Jêgirtin/Mehmed Uzun/Mirina Kalekî Rind/1987}}}}
#* {{jêder-kitêb|ku|nivîskar=w:Mehmed Uzun|sernav=w:Siya Evînê|weşanxane2=İthaki|cih2=İstanbul|sal=1989|sal2=2005|isbn2=975-273-098-1|jêgirtin=Bellê, ew jin bû ku wan ê silav lê bikirina û li ser milên wê '''veniştina'''.}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|landen|guman=erê}}, {{W+|de|setzen|guman=erê}}, {{W-|de|sich niederlassen|guman=erê}}, {{W+|de|Abscheidung|guman=erê}}, {{W-|de|Ausfällung|guman=erê}}, {{W+|de|Sitz|guman=erê}}
* {{Z|fa}}: {{W+|fa|فرود آمدن|guman=erê}}, {{W+|fa|قرار گرفتن|guman=erê}}
* {{Z|en}}: {{W+|en|berth|guman=erê}}, {{W+|en|coming down|guman=erê}}
* {{Z|tr}}: {{W+|tr|konmak|guman=erê}}, {{W-|tr|menzil tutmak|guman=erê}}, {{W+|tr|tüneklemek|guman=erê}}, {{W+|tr|tünemek|guman=erê}}, {{W+|tr|konma|guman=erê}}, {{W+|tr|konuş|guman=erê}}, {{W+|tr|tünekleme|guman=erê}}, {{W+|tr|tüneme|guman=erê}}
{{werger-bin}}
fa2287xzatu369ickwhrhn3k3ek5i3p
6334096
6334095
2026-08-16T13:53:27Z
Wikihez
7344
/* Lêker */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334096
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{kîte|ku|ve|niş|tin}}
=== Lêker ===
{{lêker|ku|c=x-ngh}}
# Bi ser derekê ve vegirtin û li ser danîn, [[li ser rûniştin]].
#* {{jêder|ku|jêgirtin=Min dizanibû ku ew dê bi kêfxweşî were nik min '''venişe''' û ava xwe vexwe, qûta xwe bixwe.|{{Jêgirtin/Mehmed Uzun/Mirina Kalekî Rind/1987}}}}
#* {{jêder-kitêb|ku|nivîskar=w:Mehmed Uzun|sernav=w:Siya Evînê|weşanxane2=w:tr:İthaki|cih2=İstanbul|sal=1989|sal2=2005|isbn2=975-273-098-1|jêgirtin=Bellê, ew jin bû ku wan ê silav lê bikirina û li ser milên wê '''veniştina'''.}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|landen|guman=erê}}, {{W+|de|setzen|guman=erê}}, {{W-|de|sich niederlassen|guman=erê}}, {{W+|de|Abscheidung|guman=erê}}, {{W-|de|Ausfällung|guman=erê}}, {{W+|de|Sitz|guman=erê}}
* {{Z|fa}}: {{W+|fa|فرود آمدن|guman=erê}}, {{W+|fa|قرار گرفتن|guman=erê}}
* {{Z|en}}: {{W+|en|berth|guman=erê}}, {{W+|en|coming down|guman=erê}}
* {{Z|tr}}: {{W+|tr|konmak|guman=erê}}, {{W-|tr|menzil tutmak|guman=erê}}, {{W+|tr|tüneklemek|guman=erê}}, {{W+|tr|tünemek|guman=erê}}, {{W+|tr|konma|guman=erê}}, {{W+|tr|konuş|guman=erê}}, {{W+|tr|tünekleme|guman=erê}}, {{W+|tr|tüneme|guman=erê}}
{{werger-bin}}
k2m9ud42r1ty0daj1p3ny25zptpq42f
fižol
0
48648
6334166
6326696
2026-08-16T17:57:49Z
Wikihez
7344
6334166
wikitext
text/x-wiki
== {{ziman|sl}} ==
=== Bilêvkirin ===
:{{ku-tîp|fîjol}}
=== Navdêr ===
{{navdêr|sl|c=m}}
# [[fasûlî]]
#* {{bêmînak|sl}}
bkf5fr2s4novow429wtt1g2dbwqrwg2
6334168
6334166
2026-08-16T17:58:44Z
Wikihez
7344
/* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334168
wikitext
text/x-wiki
== {{ziman|sl}} ==
=== Bilêvkirin ===
:{{ku-tîp|fîjol}}
=== Navdêr ===
{{navdêr|sl|c=m-in}}
# [[fasûlî]]
#* {{bêmînak|sl}}
n5rtjidpjn0lt256730hhdsh82o82dk
mêkut
0
50025
6334104
6334058
2026-08-16T14:46:11Z
Balyozbot
7867
Bot: Sererastkirina şablonan û koda zimên
6334104
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[amûr|Amûrek]] [[serikreq]] e û [[bi taybetî]] bo [[kutan]]a [[bizmar]]an [[têt]] [[bikaranîn]].
#: {{hevmane|ku|çekûç}}
#* {{bênimûne|ku}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|مێکوت}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W+|af|hamer}}
* {{Z|sq}}: {{W+|sq|çekan}}
* {{Z|de}}: {{W|de|Hammer|m}}, {{W+|de|Holzhammer}}
* {{Z|eu}}: {{W+|eu|mailu}}
* {{Z|br}}: {{W+|br|morzhol}}
* {{Z|da}}: {{W+|da|hammer}}
* {{Z|eo}}: {{W+|eo|martelo}}
* {{Z|et}}: {{W+|et|haamer}}
* {{Z|fa}}: {{W+|fa|پتک}}, {{W+|fa|پتک چوبی}}
* {{Z|fi}}: {{W+|fi|vasara}}
* {{Z|fr}}: {{W|fr|marteau|m}}
* {{Z|gl}}: {{W+|gl|martelo}}
* {{Z|nl}}: {{W|nl|hamer|m}}
* {{Z|en}}: {{W+|en|hammer}}, {{W+|en|beetle}}, {{W|en|pounder|guman=erê}}
* {{Z|ga}}: {{W+|ga|casúr}}
* {{Z|it}}: {{W|it|martello|m}}
* {{Z|is}}: {{W+|is|hamar}}
* {{Z|ca}}: {{W+|ca|martell}}
* {{Z|la}}: {{W+|la|martellus}}
* {{Z|hu}}: {{W+|hu|kalapács}}
* {{Z|no}}: {{W+|no|hammer}}
* {{Z|pap}}: {{W+|pap|martin}}
* {{Z|pl}}: {{W+|pl|młotek}}
* {{Z|pt}}: {{W+|pt|martelo}}
* {{Z|ro}}: {{W+|ro|ciocan}}
* {{Z|ru}}: {{W+|ru|молот}}
* {{Z|sh}}: {{W+|sh|čekić}}
* {{Z|sk}}: {{W+|sk|búchat}}
* {{Z|es}}: {{W|es|martillo|m}}
* {{Z|sw}}: {{W+|sw|nyundo}}
* {{Z|sv}}: {{W+|sv|hammare}}
* {{Z|tr}}: {{W+|tr|çekiç}}, {{W+|tr|tokmak}}
* {{Z|cy}}: {{W+|cy|morthwyl}}
{{werger-bin}}
gsxhew3c3d90iazvfxe1ajp6hvhhg44
gula hesretê
0
52187
6334165
6326827
2026-08-16T17:56:42Z
Wikihez
7344
+{{ku-tewîn-nav|, [[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6334165
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{e|ku|nebat}} [[nebat|Nebat]]ek [[biharat]]der e.
#: {{hevmane|ku|zaferan|hanek|sefran}}
#* {{jêder|ku|jêgirtin=Welatê '''gula hesretê''' Mezopotamya, Îran û Hindistan e. Ji wan deveran derbasî welatên din bûye û niha li welatên wek Spanya, Fransa welatên Balqanan û hinek welatên derya spî jî çandinîya wê tê kirin. Dîroka wê bi esehî ne kivş e. Tiştê tê zanîn, Papyros Ebers dibêje: “ Berî zayînê bi 1600 salan ji alîyê Misrîyan de jibo derman û tamxweşkirinê dihat xebitandin û ew jî ji Sumerîyan hîn bûne”. Heke wusa be, Sumerî jî beşek ji Gûtî û Hûrrîyan in ku ew jî bapîrên me Kurdan in.|Bavê Jîndar, Lotikxane.com, 7/2009}}
==== Tewandin ====
{{ku-tewîn-nav|heve=îzafe}}
==== Etîmolojî ====
Ji {{qf|ku|gul|hesret}}.
====Navê zanistî====
* {{q|navz|Crocus sativus}}
==== Werger ====
{{werger-ser}}
* {{Z|bg}}: {{W|bg|шафран|m}}
* {{Z|zh}}: {{W-|zh|番紅花}} / [[番红花]] (fān hóng huā)
* {{Z|ar}}: {{W-|ar|زعفران|زَعْفَرَان|tr=ze'feran}}
* {{Z|fa}}: {{W-|fa|زعفران|tr=ze'feran}}
* {{Z|fi}}: {{W+|fi|sahrami}}
* {{Z|fr}}: {{W+|fr|safran}}
* {{Z|hi}}: {{W-|hi|केसर|tr=kesar}}
* {{Z|en}}: {{W+|en|saffron}}
* {{Z|it}}: {{W|it|zafferano|m}}
* {{Z|ja}}: {{W-|ja|サフラン|tr=safuran}}
* {{Z|la}}: {{W+|la|safranum}}
* {{Z|no}}: {{W+|no|safran}}
* {{Z|pl}}: {{W|pl|szafran|m}}, {{W|pl|krokus|m}}
* {{Z|pt}}: {{W|pt|açafrão|m}}
* {{Z|ru}}: {{W-|ru|шафран|tr=šafrán}}
* {{Z|sa}}: {{W-|sa|कुङ्कुम}} [[kuṅkum]]
* {{Z|sh}}: {{W|sh|šafran#Bosnian|m}}
* {{Z|sh}}:
*: Kirîlî: [[шафран#Serbian|шафран]] {{mê}}
*: Latînî: {{W|la|šafran#Serbian|m}}
* {{Z|sl}}: {{W|sl|žafran|m}}
* {{Z|es}}: {{W|es|azafrán|m}}
* {{Z|sv}}: {{W+|sv|saffran}}
* {{Z|th}}: {{W-|th|หญ้าฝรั่น|tr=Ya Faran}}
* {{Z|tr}}: {{W+|tr|safran}}
* {{Z|ur}}: {{W|ur|کیسر|tr=kesar}}
{{werger-bin}}
10zg1ndvkgc5oqv51kbw114yoqfbrtf
6334184
6334165
2026-08-16T18:41:11Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334184
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{e|ku|nebat}} [[nebat|Nebat]]ek [[biharat]]der e.
#: {{hevmane|ku|zaferan|hanek|sefran}}
#* {{jêder|ku|jêgirtin=Welatê '''gula hesretê''' Mezopotamya, Îran û Hindistan e. Ji wan deveran derbasî welatên din bûye û niha li welatên wek Spanya, Fransa welatên Balqanan û hinek welatên derya spî jî çandinîya wê tê kirin. Dîroka wê bi esehî ne kivş e. Tiştê tê zanîn, Papyros Ebers dibêje: “ Berî zayînê bi 1600 salan ji alîyê Misrîyan de jibo derman û tamxweşkirinê dihat xebitandin û ew jî ji Sumerîyan hîn bûne”. Heke wusa be, Sumerî jî beşek ji Gûtî û Hûrrîyan in ku ew jî bapîrên me Kurdan in.|Bavê Jîndar, Lotikxane.com, 7/2009}}
==== Tewandin ====
{{ku-tewîn-nav|heve=îzafe}}
====Navê zanistî====
* {{q|navz|Crocus sativus}}
==== Etîmolojî ====
Ji {{qf|ku|gul|hesret}}.
==== Werger ====
{{werger-ser}}
* {{Z|bg}}: {{W|bg|шафран|m}}
* {{Z|zh}}: {{W-|zh|番紅花}} / [[番红花]] (fān hóng huā)
* {{Z|ar}}: {{W-|ar|زعفران|زَعْفَرَان|tr=ze'feran}}
* {{Z|fa}}: {{W-|fa|زعفران|tr=ze'feran}}
* {{Z|fi}}: {{W+|fi|sahrami}}
* {{Z|fr}}: {{W+|fr|safran}}
* {{Z|hi}}: {{W-|hi|केसर|tr=kesar}}
* {{Z|en}}: {{W+|en|saffron}}
* {{Z|it}}: {{W|it|zafferano|m}}
* {{Z|ja}}: {{W-|ja|サフラン|tr=safuran}}
* {{Z|la}}: {{W+|la|safranum}}
* {{Z|no}}: {{W+|no|safran}}
* {{Z|pl}}: {{W|pl|szafran|m}}, {{W|pl|krokus|m}}
* {{Z|pt}}: {{W|pt|açafrão|m}}
* {{Z|ru}}: {{W-|ru|шафран|tr=šafrán}}
* {{Z|sa}}: {{W-|sa|कुङ्कुम}} [[kuṅkum]]
* {{Z|sh}}: {{W|sh|šafran#Bosnian|m}}
* {{Z|sh}}:
*: Kirîlî: [[шафран#Serbian|шафран]] {{mê}}
*: Latînî: {{W|la|šafran#Serbian|m}}
* {{Z|sl}}: {{W|sl|žafran|m}}
* {{Z|es}}: {{W|es|azafrán|m}}
* {{Z|sv}}: {{W+|sv|saffran}}
* {{Z|th}}: {{W-|th|หญ้าฝรั่น|tr=Ya Faran}}
* {{Z|tr}}: {{W+|tr|safran}}
* {{Z|ur}}: {{W|ur|کیسر|tr=kesar}}
{{werger-bin}}
cy4digf5pkuf7meqqlz1ck0sh4a9wel
6334194
6334184
2026-08-16T19:12:13Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir, Valahiya beşan rast kir.)
6334194
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{e|ku|nebat}} [[nebat|Nebatek]] [[biharat]]der e.
#: {{hevmane|ku|zaferan|hanek|sefran}}
#* {{jêder|ku|jêgirtin=Welatê '''gula hesretê''' Mezopotamya, Îran û Hindistan e. Ji wan deveran derbasî welatên din bûye û niha li welatên wek Spanya, Fransa welatên Balqanan û hinek welatên derya spî jî çandinîya wê tê kirin. Dîroka wê bi esehî ne kivş e. Tiştê tê zanîn, Papyros Ebers dibêje: “ Berî zayînê bi 1600 salan ji alîyê Misrîyan de jibo derman û tamxweşkirinê dihat xebitandin û ew jî ji Sumerîyan hîn bûne”. Heke wusa be, Sumerî jî beşek ji Gûtî û Hûrrîyan in ku ew jî bapîrên me Kurdan in.|Bavê Jîndar, Lotikxane.com, 7/2009}}
==== Tewandin ====
{{ku-tewîn-nav|heve=îzafe}}
==== Navê zanistî ====
* {{q|navz|Crocus sativus}}
==== Etîmolojî ====
Ji {{qf|ku|gul|hesret}}.
==== Werger ====
{{werger-ser}}
* {{Z|bg}}: {{W|bg|шафран|m}}
* {{Z|zh}}: {{W-|zh|番紅花}} / [[番红花]] (fān hóng huā)
* {{Z|ar}}: {{W-|ar|زعفران|زَعْفَرَان|tr=ze'feran}}
* {{Z|fa}}: {{W-|fa|زعفران|tr=ze'feran}}
* {{Z|fi}}: {{W+|fi|sahrami}}
* {{Z|fr}}: {{W+|fr|safran}}
* {{Z|hi}}: {{W-|hi|केसर|tr=kesar}}
* {{Z|en}}: {{W+|en|saffron}}
* {{Z|it}}: {{W|it|zafferano|m}}
* {{Z|ja}}: {{W-|ja|サフラン|tr=safuran}}
* {{Z|la}}: {{W+|la|safranum}}
* {{Z|no}}: {{W+|no|safran}}
* {{Z|pl}}: {{W|pl|szafran|m}}, {{W|pl|krokus|m}}
* {{Z|pt}}: {{W|pt|açafrão|m}}
* {{Z|ru}}: {{W-|ru|шафран|tr=šafrán}}
* {{Z|sa}}: {{W-|sa|कुङ्कुम}} [[kuṅkum]]
* {{Z|sh}}: {{W|sh|šafran#Bosnian|m}}
* {{Z|sh}}:
*: Kirîlî: [[шафран#Serbian|шафран]] {{mê}}
*: Latînî: {{W|la|šafran#Serbian|m}}
* {{Z|sl}}: {{W|sl|žafran|m}}
* {{Z|es}}: {{W|es|azafrán|m}}
* {{Z|sv}}: {{W+|sv|saffran}}
* {{Z|th}}: {{W-|th|หญ้าฝรั่น|tr=Ya Faran}}
* {{Z|tr}}: {{W+|tr|safran}}
* {{Z|ur}}: {{W|ur|کیسر|tr=kesar}}
{{werger-bin}}
dsqzrixyelq6f20sgeqrx1j4wir36pm
6334237
6334194
2026-08-16T21:15:39Z
Wikihez
7344
/* Werger */
6334237
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{e|ku|nebat}} [[nebat|Nebatek]] [[biharat]]der e.
#: {{hevmane|ku|zaferan|hanek|sefran}}
#* {{jêder|ku|jêgirtin=Welatê '''gula hesretê''' Mezopotamya, Îran û Hindistan e. Ji wan deveran derbasî welatên din bûye û niha li welatên wek Spanya, Fransa welatên Balqanan û hinek welatên derya spî jî çandinîya wê tê kirin. Dîroka wê bi esehî ne kivş e. Tiştê tê zanîn, Papyros Ebers dibêje: “ Berî zayînê bi 1600 salan ji alîyê Misrîyan de jibo derman û tamxweşkirinê dihat xebitandin û ew jî ji Sumerîyan hîn bûne”. Heke wusa be, Sumerî jî beşek ji Gûtî û Hûrrîyan in ku ew jî bapîrên me Kurdan in.|Bavê Jîndar, Lotikxane.com, 7/2009}}
==== Tewandin ====
{{ku-tewîn-nav|heve=îzafe}}
==== Navê zanistî ====
* {{q|navz|Crocus sativus}}
==== Etîmolojî ====
Ji {{qf|ku|gul|hesret}}.
==== Werger ====
{{werger-ser}}
* {{Z|bg}}: {{W|bg|шафран|m}}
* {{Z|zh}}: {{W-|zh|番紅花}} / [[番红花]] (fān hóng huā)
* {{Z|ar}}: {{W-|ar|زَعْفَرَان|tr=ze'feran}}
* {{Z|fa}}: {{W-|fa|زعفران|tr=ze'feran}}
* {{Z|fi}}: {{W+|fi|sahrami}}
* {{Z|fr}}: {{W+|fr|safran}}
* {{Z|hi}}: {{W-|hi|केसर|tr=kesar}}
* {{Z|en}}: {{W+|en|saffron}}
* {{Z|it}}: {{W|it|zafferano|m}}
* {{Z|ja}}: {{W-|ja|サフラン|tr=safuran}}
* {{Z|la}}: {{W+|la|safranum}}
* {{Z|no}}: {{W+|no|safran}}
* {{Z|pl}}: {{W|pl|szafran|m}}, {{W|pl|krokus|m}}
* {{Z|pt}}: {{W|pt|açafrão|m}}
* {{Z|ru}}: {{W-|ru|шафран|tr=šafrán}}
* {{Z|sa}}: {{W-|sa|कुङ्कुम}} [[kuṅkum]]
* {{Z|sh}}: {{W|sh|šafran#Bosnian|m}}
* {{Z|sh}}:
*: Kirîlî: [[шафран#Serbian|шафран]] {{mê}}
*: Latînî: {{W|la|šafran#Serbian|m}}
* {{Z|sl}}: {{W|sl|žafran|m}}
* {{Z|es}}: {{W|es|azafrán|m}}
* {{Z|sv}}: {{W+|sv|saffran}}
* {{Z|th}}: {{W-|th|หญ้าฝรั่น|tr=Ya Faran}}
* {{Z|tr}}: {{W+|tr|safran}}
* {{Z|ur}}: {{W|ur|کیسر|tr=kesar}}
{{werger-bin}}
1caic07one550mmrym8a3q82tj58hlc
nava hefteyê
0
57933
6334268
6328066
2026-08-16T23:23:33Z
Wikihez
7344
/* Navdêr */ ew ê tevlihevî bi botan çêke (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334268
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=-|p=nava hefteyan}}
# [[roj]]ên [[kar]]kirinê, ji [[roj]]a [[duşem]]ê heta [[în]]ê (lê ne [[şemî]] û [[yekşem]]an)
#* {{jêder|ku|jêgirtin=Li hemû bajaran dê der barê şewba berazan de perwerdehî bê dayîn. Derziya şewba berazan dê di '''nava vê hefteyê''' de bê kirin. Li Tirkiyeyê dê 16 milyon xwendekar bên derzîkirin. Li Enqereyê alarma şewba berazan didome. Bûyerên şewba berazan hata niha wekî 16 tê zanîn.|Welat.com, 10/2009}}
==== Formên alternatîv ====
* {{g|ku|nava heftiyê}}
* {{g|ku|nav hefteyê}}
* {{g|ku|nav heftiyê}}
==== Dijmane ====
* {{g|ku|dawiya hefteyê}}
==== Etîmolojî ====
Ji {{heve|ku|nava|hefteyê}}.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W|de|Wochentag}} {{n}}
* {{Z|eu}}: {{W|eu|astegun|xs=Basque}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W|cmn|平日|tr=píngrì}}, {{W|cmn|平常日|tr=píngchángrì}}
* {{Z|da}}: {{W|da|almindelig ugedag}}
* {{Z|id}}: {{W|id|hari biasa|xs=Indonesian}}
* {{Z|eo}}: {{W|eo|semajntago}}
* {{Z|fi}}: {{W|fi|arkipäivä}}
* {{Z|fr}}: {{W|fr|jour de la semaine}} {{mê}}
* {{Z|nl}}: {{W|nl|werkdag}} {{n}}
* {{Z|en}}: {{W-|en|[[weekday]]s}}
* {{Z|it}}: {{W|it|giorno feriale}} {{n}}
* {{Z|ja}}: {{W|ja|平日|tr=heijitsu}}, {{W|ja|ウィークデー|tr=wīkudē}}
* {{Z|ca}}: {{W|ca|dia feiner}}, {{W|ca|dia laborable}}
* {{Z|ko}}: {{W|ko|평일|tr=pyeongil|sc=Hang}} ({{W|ko|平日|sc=Hani}})
* {{Z|mk}}: {{W|mk|делник|tr=délnik}} {{n}}
* {{Z|ro}}: {{W|ro|zi a săptămânii}} {{mê}}, {{W|ro|zi lucrătoare}} {{mê}}
* {{Z|ru}}: {{W|ru|рабочий день|tr=rabóčij den'}} {{n}}, {{W|ru|будний день|tr=búdnij den'}} {{n}}, {{W|ru|будни|tr=búdni|p}}
* {{Z|sv}}: {{W-|sv|[[vardag]]arna}}
* {{Z|el}}: {{W|el|καθημερινή}} {{mê}}
{{werger-bin}}
ho0b36elmgcc7blzdutlcqunhroex2s
rabirdû
0
59053
6334296
6326811
2026-08-17T09:31:15Z
Rewiye tine
7120
6334296
wikitext
text/x-wiki
== {{ziman|ckb}} ==
=== Navdêr ===
{{navdêr|ckb|sc=Latn}}
# [[raborî]], [[borî]]
# [[jiyan]]a kesekî ya berî niha
==== Etîmolojî ====
{{bêetîmolojî|ckb}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Vergangenheit}} {{mê}}
* {{Z|fa}}: {{W+|fa|ماضی}}, {{W+|fa|گذشته}}
* {{Z|en}}: (1) {{W+|en|past}}, (2) {{W+|en|background}}
* {{Z|tr}}: {{W+|tr|geçmiş}}, {{W+|tr|mazi}}
{{werger-bin}}
prczukv7n8ud8ibx3r6c1931ygq24ar
parîz
0
63221
6334269
6334041
2026-08-16T23:27:44Z
Wikihez
7344
/* Têkildar */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334269
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[baxçe]], [[gulistan]]
#* {{jêder|ku|jêgirtin=Ne gul û ne kulîlk man<br>Ne '''parîz''' û gulistan<br>Ne dengê xweş ne meqam<br>Pêve j’ah û [[intiqam]]|Kamuran Bedirxan, helbesta ''Birîn'', jêwergirtin Celadet.com, 4/2012}}
# [[biheşt]]
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Têkildar ====
* {{g|ku|pirêz}}
==== Bide ber ====
* {{g|ku|parêz}}
==== Etîmolojî ====
Hevreha [[pirêz]] ([[baxçe]]), bide ber avestayî "peyrideêze" (baxçe, gulistan, bihişt) ji "peyri-" ([[dor]], [[dorber]], dorûber, dewrûdor) + "deêze" ([[dîwar]]) anku "cihê dîwarkirî, cihê ku dîwarek yan sûrek li dor hatiye avakirin daku wê biparêze) ku serekaniya peyva îngilîzî [[paradise]] (bihişt) û ya gelek zimanên din jî yên ewropî ye yên bi eynî maneyê ye.
bjpx57lmsn8ckvv6uh359gnydv2b8p6
6334277
6334269
2026-08-16T23:41:07Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334277
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[baxçe]], [[gulistan]]
#* {{jêder|ku|jêgirtin=Ne gul û ne kulîlk man<br>Ne '''parîz''' û gulistan<br>Ne dengê xweş ne meqam<br>Pêve j’ah û [[intiqam]]|Kamuran Bedirxan, helbesta ''Birîn'', jêwergirtin Celadet.com, 4/2012}}
# [[biheşt]]
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Têkildar ====
* {{g|ku|pirêz}}
==== Etîmolojî ====
Hevreha [[pirêz]] ([[baxçe]]), bide ber avestayî "peyrideêze" (baxçe, gulistan, bihişt) ji "peyri-" ([[dor]], [[dorber]], dorûber, dewrûdor) + "deêze" ([[dîwar]]) anku "cihê dîwarkirî, cihê ku dîwarek yan sûrek li dor hatiye avakirin daku wê biparêze) ku serekaniya peyva îngilîzî [[paradise]] (bihişt) û ya gelek zimanên din jî yên ewropî ye yên bi eynî maneyê ye.
==== Bide ber ====
* {{g|ku|parêz}}
9vosbcrwcdbaq4nyeln25eb1kcrmvnp
iş arkadaşı
0
144700
6334086
5798499
2026-08-16T11:59:01Z
Rewiye tine
7120
/* {{ziman|tr}} */
6334086
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[hevkar]], [[hevalkar]]
s4xbjreygrbtb41u1c8f0edny7dof9g
kurabiye
0
150874
6334244
4617526
2026-08-16T21:52:05Z
Zerwanda
12183
6334244
wikitext
text/x-wiki
== {{ziman|tr}} ==
=== Navdêr ===
{{navdêr|tr}}
# [[kulîçe]]
pqawcekm75o6jgy6272sjd49vvntdad
خانه و زندگی
0
254083
6334286
5991651
2026-08-17T08:16:22Z
Hamid Hassani
3683
Hamid Hassaniî navê [[خانه وزندگی]] weke [[خانه و زندگی]] guhart: + space
5991651
wikitext
text/x-wiki
== {{ziman|fa}} ==
=== Navdêr ===
{{navdêr|fa}}
# [[dirik]]
335rh8n98bmn9ywdoof5i9k89r4klvc
çareserkirin
0
262487
6334159
6321676
2026-08-16T17:44:49Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334159
wikitext
text/x-wiki
{{bnr-l|çareser kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[çarekirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|چارهسهرکرن}}
==== Etîmolojî ====
Ji {{pêk|ku|çareser|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Analysis|f|guman=erê}}
* {{Z|ar}}: {{W-|ar|معالجة}}
* {{Z|en}}: {{W+|en|analysis}}, {{W+|en|detach}}, {{W+|en|dissolve}}, {{W+|en|loosen}}, {{W+|en|solve}}, {{W+|en|unfasten}}, {{W+|en|unfix}}, {{W+|en|deconfliction|guman=erê}}
* {{Z|tr}}: {{W+|tr|çözme}}, {{W+|tr|halletme}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
3xw33gl9ff471zis8iienpxl7vt51c9
raberîkirin
0
265098
6334129
6316159
2026-08-16T17:13:24Z
Wikihez
7344
[[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6334129
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m|lk=raberî kirin}}
# [[pêşengîkirin]], [[serkêşîkirin]], [[rêzanîkirin]], [[rêberîkirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Etîmolojî ====
Ji {{pêk|ku|raberî|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|guidance}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
i1sj7wjjdwvp3bxvp3jhcoc0szko4qt
serederîkirin
0
265366
6334134
6317426
2026-08-16T17:17:53Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334134
wikitext
text/x-wiki
{{bnr-l|serederî kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[danûstandin]] pêra kirin, [[ti'amil]]kirin<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Etîmolojî ====
Ji {{pêk|ku|serederî|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: to {{W+|en|treat}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
d8ndw2f2jjwlm7lmrwrzqlm6rp3ragy
silavkirin
0
265478
6334135
6317771
2026-08-16T17:18:15Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334135
wikitext
text/x-wiki
{{bnr-l|silav kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[pirskirin]] a [[rewş]]a [[yek]]î , [[merheba]]yî, [[selam]][[kirin]], çewanî ba-îdigel hev kirin, daxazkirina xe-îye<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|سلاڤکرن}}
==== Etîmolojî ====
Ji {{pêk|ku|silav|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W+|ar|السلام}}, {{W-|ar|التسلم}}, {{W+|ar|تحية}}
* {{Z|fa}}: {{W+|fa|درود}}
* {{Z|en}}: {{W+|en|greeting}}, {{W+|en|saluting}}
* {{Z|sdh}}: {{W|sdh|سەڵام کردن}}
* {{Z|tr}}: {{W+|tr|selâmlama}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
08ad8feiza3faeo6yxk9rv1zhzqv56w
sinetkirin
0
265491
6334136
6317810
2026-08-16T17:18:27Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334136
wikitext
text/x-wiki
{{bnr-l|sinet kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[birîn]] yan [[jêvekirin]]a [[çerm]]kê [[zêde]] ji [[endam]]ê mirovan yê nêrîne yê mîzî<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|سنەتکرن}}
==== Etîmolojî ====
Ji {{pêk|ku|sinet|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Beschneidung|f|guman=erê}}
* {{Z|ar}}: {{W-|ar|الختان}}, {{W-|ar|الإختان|guman=erê}}, {{W-|ar|الإختتان|guman=erê}}, {{W-|ar|التطهیـر|guman=erê}},
* {{Z|en}}: {{W+|en|circumcision}}, {{W+|en|circumcizing|guman=erê}}
* {{Z|tr}}: {{W+|tr|hitan}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
6iztva0wa5oc9zkrmy3jba3svc5ndul
şaşîkirin
0
265820
6334160
6322769
2026-08-16T17:45:03Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334160
wikitext
text/x-wiki
{{bnr-l|şaşî kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[xeletîkirin]], [[çewtîkirin]], [[xirabî]]kirin, [[serpêçî]]kirin, [[guneh]]kirin, [[çewtîkirin]], [[serdaçûn]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Etîmolojî ====
Ji {{pêk|ku|şaşî|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|mistake}}, {{W+|en|sin}}
* {{Z|tr}}: {{W+|tr|yanılma}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
d9t7z1ncttu9o1e4hvtbbpmn7cy5o2t
tawankirin
0
266034
6334137
6318476
2026-08-16T17:18:37Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334137
wikitext
text/x-wiki
{{bnr-l|tawan kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[tomet]]kirin, [[xirabîkirin]], [[cerîme]]kirin, [[cînayet]]kirin, [[gunehkirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|تاوانکرن}}
==== Etîmolojî ====
Ji {{pêk|ku|tawan|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|plaint}}, {{W+|en|rap}}, {{W+|en|crime}}, {{W+|en|felony}}, {{W+|en|perpetrating|guman=erê}}, {{W+|en|perpetration|guman=erê}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
4vfegfke4kumd8lum25r5ljk9n3ylqg
têwerkirin
0
266157
6334138
6319535
2026-08-16T17:18:54Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334138
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[ber]]ek [[dar]]ek û htd bi havêjî yekî, [[havêtin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Etîmolojî ====
* [[tê]] yan ''(di) + [[cînav]] bo mînak (wî)'' + [[werkirin]]
==== Werger ====
{{werger-ser}}
* {{Z|en}}: to [[throw]] with a target in view
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
61ric87bv1i9rj6k2qm6eqnlu95ekqu
wexerkirin
0
266364
6334139
6320079
2026-08-16T17:19:35Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334139
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[koçkirin]], [[barkirin]], [[malavayîkirin]], [[mirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|وهغهركرن}}
==== Etîmolojî ====
Ji {{qertaf|ku|wexer|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|الإرتحال|guman=erê}}, {{W-|ar|الرحیل|guman=erê}}, {{W-|ar|مسافرة|guman=erê}}
* {{Z|en}}: {{W+|en|departure}}, {{W+|en|leaving}}, {{W+|en|travel}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
jg3devswpj3zi5rbnjelzsh8tjmb57h
windakirin
0
266373
6334140
6320120
2026-08-16T17:19:51Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334140
wikitext
text/x-wiki
{{bnr-l|winda kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[hindakirin]], [[berzekirin]], ne [[berçavkirin]], ne [[xwiyakirin]], ne [[diyarkirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|ونداکرن}}
==== Etîmolojî ====
Ji {{pêk|ku|winda|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|losing}}
* {{Z|tr}}: {{W+|tr|fevt}}, {{W+|tr|kaybetme}}, {{W+|tr|kayıp}}, {{W+|tr|yitirme}}, {{W+|tr|yenilgi}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
p9mmpmirhww7xk2q1h11mcpdqtyeaq2
xaçkirin
0
266392
6334146
6320255
2026-08-16T17:24:43Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334146
wikitext
text/x-wiki
{{bnr-l|xaç kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# kêşan xaçê, xaç pêvekirin<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Etîmolojî ====
Ji {{pêk|ku|xaç|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|صلب}}
* {{Z|en}}: {{W+|en|crucifixion}}, {{W+|en|decussating|guman=erê}}, {{W+|en|crucifying|guman=erê}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
qrosyor8qf1ejj2p14507hktfyxw4al
xelatkirin
0
266422
6334147
6320302
2026-08-16T17:24:56Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334147
wikitext
text/x-wiki
{{bnr-l|xelat kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[xelat]]ekî [[ji bo]] kesekî ku [[kar]]ekê [[çak]] kiriye dikin [[diyarî]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خهلاتكرن}}
==== Etîmolojî ====
Ji {{pêk|ku|xelat|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|الأجارة|guman=erê}}, {{W-|ar|التكرمة|guman=erê}}, {{W-|ar|التكریم|guman=erê}}, {{W-|ar|المجازات|guman=erê}}, {{W-|ar|مكافاة|guman=erê}}
* {{Z|en}}: {{W+|en|awarding}}, {{W+|en|conferment|guman=erê}}, {{W+|en|rewarding|guman=erê}}
* {{Z|tr}}: {{W+|tr|ödüllendirme}}, {{W+|tr|mükâfatını görmek}}, {{W+|tr|mükafatlandırma|guman=erê}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
jcyk3pfoyk1pinczw6nzwjcdru2o79o
xirvekirin
0
266509
6334148
6320551
2026-08-16T17:25:15Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334148
wikitext
text/x-wiki
{{bnr-l|xirve kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[car]]ek [[din]] li hev [[xir kirin]], [[komkirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خرڤهكرن}}
==== Etîmolojî ====
Ji {{pêk|ku|xirve|kirin}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}:{{W-|ar|أقفل|guman=erê}}, {{W-|ar|الإحتشاد|guman=erê}}, {{W-|ar|الإكتناز|guman=erê}}, {{W-|ar|التكثیف|guman=erê}}, {{W-|ar|الحشر|guman=erê}}, {{W-|ar|النجش|guman=erê}}, {{W-|ar|جمر|guman=erê}}
* {{Z|en}}: {{W+|en|collection}}, {{W+|en|gathering}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
il7e5bykn8hf5pnrp33oclap6uivewi
xwecihkirin
0
266585
6334156
6320724
2026-08-16T17:43:10Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334156
wikitext
text/x-wiki
{{bnr-l|xwecih kirin}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[akincîkirin]], [[binecihkirin]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خوەجهکرن}}
==== Werger ====
{{werger-ser}}
* {{Z|ar}}:{{W-|ar|الإسكان}}, {{W-|ar|الإقرار}}, {{W-|ar|التثبیت}}, {{W-|ar|التمكین|guman=erê}}, {{W-|ar|التوطین|guman=erê}}
* {{Z|en}}: {{W+|en|settle}}, {{W+|en|set}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
ad27bpxs4i5orwlr5sizir2xk576yqw
zirbarbûn
0
266776
6334157
6321384
2026-08-16T17:43:28Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334157
wikitext
text/x-wiki
{{bnr-l|zirbar bûn}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[bêbar]][[bûn]], bê[[pişk]][[bûn]], [[zirbehirbûn]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|زرباربوون}}
==== Etîmolojî ====
Ji {{pêk|ku|zirbar|bûn}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|حرمان}}
* {{Z|en}}: {{W+|en|deprivation}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
27c18p4w1mx94rh992cr4ye9pqq7r4w
6334186
6334157
2026-08-16T18:41:17Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: rêzkirina beşan
6334186
wikitext
text/x-wiki
{{bnr-l|zirbar bûn}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[bêbar]][[bûn]], bê[[pişk]][[bûn]], [[zirbehirbûn]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|زرباربوون}}
==== Etîmolojî ====
Ji {{pêk|ku|zirbar|bûn}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|حرمان}}
* {{Z|en}}: {{W+|en|deprivation}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
r4zq2z3w8oe0rjx1i1rcpz2jpr730n9
6334188
6334186
2026-08-16T18:53:43Z
Wikihez
7344
/* Kurmancî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334188
wikitext
text/x-wiki
{{bnr-l|zirbar bûn}}
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[bêbar]][[bûn]], bê[[pişk]][[bûn]], [[zirbehirbûn]]<ref>{{Ferhenga Kamêran}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|زرباربوون}}
==== Etîmolojî ====
Ji {{pêk|ku|zirbar|bûn}}.
==== Werger ====
{{werger-ser}}
* {{Z|ar}}: {{W-|ar|حرمان}}
* {{Z|en}}: {{W+|en|deprivation}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
kj1kh107rquge3idafv8ui4kyyfabsb
bellows
0
296804
6334189
6091453
2026-08-16T18:53:58Z
Zerwanda
12183
6334189
wikitext
text/x-wiki
== {{ziman|en}} ==
=== Bilêvkirin ===
* {{deng|en|en-us-bellows.ogg|dever=DYA}}
=== Navdêr ===
{{navdêr|en}}
# [[nepax]]
# [[pişik]]
lq9czz8ank3d6f59nsrjn0gh4ccddwk
remorse
0
315500
6334290
5859869
2026-08-17T08:20:23Z
Rewiye tine
7120
/* Navdêr */
6334290
wikitext
text/x-wiki
== {{ziman|en}} ==
=== Navdêr ===
{{navdêr|en}}
# [[nerihetiya wijdanê]], [[poşmanî]], [[tobekarî]]
5xe9pbmar9np67hvjfk3q0ibsqy5dfm
6334293
6334290
2026-08-17T09:20:03Z
Rewiye tine
7120
/* Navdêr */
6334293
wikitext
text/x-wiki
== {{ziman|en}} ==
=== Navdêr ===
{{navdêr|en}}
# [[nerihetiya wijdanê]], [[poşmanî]], [[tobekarî]], [[ezeba wîcdanê]]
1ffs3z0da4u8ni3jyephiksyb722wfs
perxaç
0
342738
6334201
6285684
2026-08-16T19:52:50Z
Zerwanda
12183
/* Etîmolojî */
6334201
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[kêk]], [[kulîçe]], [[kulêre]], [[paste]], [[kulîparçe]], [[poxaça]]
==== Etîmolojî ====
Hevreh bi {{hv|tr|pargaç}}, {{hv|hu|pogácsa}}, ji zimanekî slavî; bide ber {{hv|sh|pogača}}, {{hv|bg|погача}}, {{hv|mk|погача|w=nanê şkeva}}, ji {{hv|la|focācius|w=ya ocaxê}}, ji {{q|la|focus|w=ocax}}, çumkî li wextên pêş-nûjen li ser xweliya tifikê dihatin pêjan. {{cotreh|ku|poxaça}}.
==== Werger ====
{{werger-ser}}
* {{Z|tr}}: {{W+|tr|poğaça}}
{{werger-bin}}
8jol4emnm500pw8wnph96mgvkfutl1i
poxaça
0
342739
6334203
6286101
2026-08-16T19:53:27Z
Zerwanda
12183
/* Etîmolojî */
6334203
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[kêk]], [[kulîçe]], [[kulêre]], [[paste]], [[kulîparçe]], [[perxaç]]
==== Etîmolojî ====
Ji {{deyn|tr|ku|poğaça}}
==== Werger ====
{{werger-ser}}
* {{Z|tr}}: {{W+|tr|poğaça}}
{{werger-bin}}
3qs44ob2n6e0mtlx4uo5g5tt3ym81a1
6334205
6334203
2026-08-16T19:56:21Z
Zerwanda
12183
/* Etîmolojî */
6334205
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[kêk]], [[kulîçe]], [[kulêre]], [[paste]], [[kulîparçe]], [[perxaç]]
==== Etîmolojî ====
Ji {{deyn|tr|ku|poğaça}}, ku ji zimanekî ewropî ye. Bide ber {{hv|hu|pogácsa}}, {{hv|sh|pogača}}, {{hv|bg|погача|w=nanê şkeva}}, {{hv|mk|погача|w=nanê şkeva}}, {{hv|it|foccaccia}}, {{hv|es|hogaza}}, {{hv|pt|fogaça}}, {{hv|fr|fougasse}}. Hemû ji {{hv|la|focācius|w=ocaxî, ya argûnan}}, ji {{q|la|focus|w=ocax}}.
==== Werger ====
{{werger-ser}}
* {{Z|tr}}: {{W+|tr|poğaça}}
{{werger-bin}}
mvtmzv3k35ks5o07a4ea239xt2y4apr
mefreq
0
427617
6334271
6333572
2026-08-16T23:30:53Z
Wikihez
7344
[[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6334271
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[maden|Maden]]ek [[amêjen]] e û ji [[mis]]î û [[bi kêmî]] [[pîl]]ê hatiye [[çêkirin]].
#: {{hevmane|ku|tunc|bironz}}
==== Formên alternatîv ====
* {{g|ku|mifrûq}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W|af|brons}}
* {{Z|de}}: {{W|de|Bronze|f}}, {{W|de|bronze}}
* {{Z|da}}: {{W|da|bronze}}
* {{Z|ar}}: {{W-|ar|برونز|tr=brûnz}}
* {{Z|eo}}: {{W|eo|bronzo}}
* {{Z|fi}}: {{W|fi|pronssi}}
* {{Z|fr}}: {{W|fr|bronze}}, {{W|fr|airain}}
* {{Z|fy}}: {{W|fy|brûns}}
* {{Z|nl}}: {{W|nl|brons}}
* {{Z|en}}: {{W|en|bronze}}
* {{Z|it}}: {{W|it|bronzo}}
* {{Z|ca}}: {{W|ca|bronze}}
* {{Z|la}}: {{W|la|aes}}
* {{Z|hu}}: {{W|hu|bronz}}
* {{Z|pap}}: {{W|pap|bròns}}
* {{Z|pt}}: {{W|pt|bronze}}
* {{Z|ru}}: {{W|ru|бронза}}
* {{Z|es}}: {{W|es|bronce}}
* {{Z|sv}}: {{W|sv|brons}}
* {{Z|tr}}: {{W|tr|tunç}}, {{W|tr|bronz}}
* {{Z|el}}: {{W|el|μπρούντζος}}
{{werger-bin}}
nxgqxoyopzjpdqxv9glufy823byiqdj
6334281
6334271
2026-08-17T01:12:07Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir.)
6334281
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=n}}
# [[maden|Madenek]] [[amêjen]] e û ji [[mis]]î û [[bi kêmî]] [[pîl]]ê hatiye [[çêkirin]].
#: {{hevmane|ku|tunc|bironz}}
==== Formên alternatîv ====
* {{g|ku|mifrûq}}
==== Etîmolojî ====
{{bêetîmolojî|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W|af|brons}}
* {{Z|de}}: {{W|de|Bronze|f}}, {{W|de|bronze}}
* {{Z|da}}: {{W|da|bronze}}
* {{Z|ar}}: {{W-|ar|برونز|tr=brûnz}}
* {{Z|eo}}: {{W|eo|bronzo}}
* {{Z|fi}}: {{W|fi|pronssi}}
* {{Z|fr}}: {{W|fr|bronze}}, {{W|fr|airain}}
* {{Z|fy}}: {{W|fy|brûns}}
* {{Z|nl}}: {{W|nl|brons}}
* {{Z|en}}: {{W|en|bronze}}
* {{Z|it}}: {{W|it|bronzo}}
* {{Z|ca}}: {{W|ca|bronze}}
* {{Z|la}}: {{W|la|aes}}
* {{Z|hu}}: {{W|hu|bronz}}
* {{Z|pap}}: {{W|pap|bròns}}
* {{Z|pt}}: {{W|pt|bronze}}
* {{Z|ru}}: {{W|ru|бронза}}
* {{Z|es}}: {{W|es|bronce}}
* {{Z|sv}}: {{W|sv|brons}}
* {{Z|tr}}: {{W|tr|tunç}}, {{W|tr|bronz}}
* {{Z|el}}: {{W|el|μπρούντζος}}
{{werger-bin}}
p9co7grv2lakpig8nfbweik0x0rt21d
bersorik
0
467783
6334267
6327463
2026-08-16T23:22:41Z
Wikihez
7344
6334267
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:Rotkehlchenportrait aus ca. 1 Meter Entfernung.jpg|250px|thumb|rast|bersorikek]]
{{navdêr|ku|z=m}}
# {{e|ku|teyr}} [[cure]]yek ji [[çûk]]ên [[mêşgiran]] (''Muscicapidae'') e
==== Formên alternatîv ====
* [[bersork]]
==== Hevmane ====
* [[bûkesor]]
* [[gerdensor]]
* [[sîngsor]]
==== Têkildar ====
* [[bûknefsok]]
* [[bilbil]]
==== Etîmolojî ====
Ji {{qertaf|ku|ber|sorik}}.
==== Werger ====
{{werger-ser}}
* {{Z|an}}: {{W-|an|papirroi}}
* {{Z|ast}}: {{W-|ast|raitán}}
* {{Z|eu}}: {{W-|eu|txantxangorri}}
* {{Z|br}}: {{W+|br|boc'hruz}}
* {{Z|bg}}: {{W-|bg|червеногръдка}} {{mê}}
* {{Z|cs}}: {{W+|cs|červenka obecná}} {{mê}}
* {{Z|da}}: {{W+|da|rødhals}}, {{W-|da|rødkælken}}
* {{Z|eo}}: {{W-|eo|rubekolo}}, {{W-|eo|ruĝgorĝulo}}
* {{Z|et}}: {{W+|et|punarind}}
* {{Z|fi}}: {{W+|fi|punarinta}}
* {{Z|frp}}: {{W-|frp|petro-rojo}}
* {{Z|fr}}: {{W+|fr|rouge-gorge|m}}
* {{Z|fy}}: {{W+|fy|readboarstke}}
* {{Z|gd}}: {{W-|gd|brù-dhearg}}
* {{Z|gl}}: {{W+|gl|paporroibo}}, {{W+|gl|paporrubio}}
* {{Z|ka}}: {{W-|ka|გულწითელა}}
* {{Z|nl}}: {{W+|nl|roodborstje}}
* {{Z|he}}: {{W-|he|אדום החזה}}
* {{Z|io}}: {{W+|io|redpektoro}}
* {{Z|en}}: {{W+|en|robin}}, {{W+|en|robin redbreast}}, {{W+|en|European robin}}
* {{Z|it}}: {{W+|it|pettirosso|m}}
* {{Z|ja}}: {{W+|ja|駒鳥|tr=コマドリ, komadori}}
* {{Z|ca}}: {{W+|ca|pit-roig}}, {{W-|ca|rupit}}, {{W-|ca|ropit}}
* {{Z|co}}: {{W+|co|pettirossu}}, {{W-|co|pettirussu}}
* {{Z|lv}}: {{W-|lv|sarkanrīklīte}}
* {{Z|lt}}: {{W-|lt|riepsnelė}}
* {{Z|hu}}: {{W+|hu|vörösbegy}}
* {{Z|no}}: {{W+|no|rødstrupe}}
* {{Z|oc}}: {{W+|oc|papach-rós}}, {{W+|oc|pança-roja}}
* {{Z|pl}}: {{W+|pl|rudzik|m}}, {{W+|pl|raszka}} {{mê}}
* {{Z|pt}}: {{W-|pt|toutinegra}}
* {{Z|ro}}: {{W+|ro|măcăleandru}}
* {{Z|ru}}: {{W+|ru|зарянка}} {{mê}}, {{W+|ru|малиновка}} {{mê}}
* {{Z|sh}}: {{W+|sh|crvendać}}
* {{Z|sk}}: {{W-|sk|slávik červienka}}
* {{Z|sl}}: {{W+|sl|taščica}} {{mê}}
* {{Z|es}}: {{W+|es|petirrojo|m}}
* {{Z|sv}}: {{W+|sv|rödhake}}
* {{Z|tr}}: {{W+|tr|kızılgerdan}}
* {{Z|cy}}: {{W-|cy|Robin Goch}}
{{werger-bin}}
ib4m9n2thox0t8r9gzolkz1y8dph18e
Modul:rêziman
828
517740
6334115
6115355
2026-08-16T15:11:18Z
Wikihez
7344
6334115
Scribunto
text/plain
insert = table.insert
local process_params = require("Module:parameters").process
local export = {}
local function ferhengok(entry, text)
text = text or entry
return "[[Nimînok:Ferhengok#" .. entry .. "|" .. text .. "]]"
end
local function get_args(frame)
local boolean = {type = "boolean"}
local boolean_list_allow_holes = {type = "boolean", list = true, allow_holes = true}
local list_allow_holes = {list = true, allow_holes = true}
return process_params(frame:getParent().args, {
[1] = {required = true, type = "language", default = "und"},
["sc"] = {type = "script"},
["sort"] = true,
["sckat"] = boolean,--sccat
["sccat"] = {boolean, alias_of = "sckat"},
["nekatcure"] = boolean,--noposcat
["noposcat"] = {boolean, alias_of = "nekatcure"},
["nekatçendterm"] = boolean,--nomultiwordcat
["nomultiwordcat"] = {boolean, alias_of = "nekatçendterm"},
["nekatcins"] = boolean,--nogendercat
["nogendercat"] = {boolean, alias_of = "nekatcins"},
["nekatpalîndrom"] = boolean,--nopalindromecat
["nopalindromecat"] = {boolean, alias_of = "nekatpalîndrom"},
["serlînkneke"] = boolean,--nolinkhead
["nolinkhead"] = {boolean, alias_of = "serlînkneke"},
["neformtr"] = boolean,
["form"] = true,
["formaalt"] = boolean, --altform EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
[2] = list_allow_holes,
["cuda"] = list_allow_holes,
["id"] = true,
["tr"] = list_allow_holes,
["ts"] = list_allow_holes,
["gloss"] = true, -- bo çi ye? En.wikt bi kar neyîne
["c"] = {list = true}, --ji bo lêkeran cure, ji bo navdêran cins(iyet)
["c\1etîk"] = list_allow_holes, --etîketek ji bo cins(iyet)
["c\1ref"] = list_allow_holes,--çavkanî ji bo cins(iyet)
["z"] = {list = true, alias_of = "c"},
["z\1qual"] = {list = true, alias_of = "c\1etîk"},
["z\1ref"] = {list = true, alias_of = "c\1ref"},
["j"] = {list = true}, -- jimar
["m"] = list_allow_holes,
["m\1tr"] = list_allow_holes,
["n"] = list_allow_holes,
["n\1tr"] = list_allow_holes,
["nt"] = list_allow_holes,
["nt\1tr"] = list_allow_holes,
["p"] = list_allow_holes,
["p\1tr"] = list_allow_holes,
["y"] = list_allow_holes,
["y\1tr"] = list_allow_holes,
["np"] = list_allow_holes,-- forma nêr ya pirjimar
["np\1tr"] = list_allow_holes,
["mp"] = list_allow_holes,-- forma mê ya pirjimar
["mp\1tr"] = list_allow_holes,
["lk"] = list_allow_holes, --forma lêker
["nd"] = list_allow_holes, --forma navdêr
["niha"] = list_allow_holes, -- rehê dema niha
["niha\1tr"] = list_allow_holes,
["borî"] = list_allow_holes, -- rehê dema borî
["borî\1tr"] = list_allow_holes,
["subj"] = list_allow_holes, -- rehê dema bilanî
["subj\1tr"] = list_allow_holes,
["fniha"] = list_allow_holes, -- forma dema niha
["fniha\1tr"] = list_allow_holes,
["fborî"] = list_allow_holes, -- forma dema borî
["fborî\1tr"] = list_allow_holes,
["fsubj"] = list_allow_holes, -- forma dema bilanî
["fsubj\1tr"] = list_allow_holes,
["sûp"] = list_allow_holes, -- forma sûperlatîv
["sûp\1tr"] = list_allow_holes,
["komp"] = list_allow_holes, -- forma komparatîv
["komp\1tr"] = list_allow_holes,
["gir"] = list_allow_holes, -- herfa mezin
["gir\1tr"] = list_allow_holes,
["hûr"] = list_allow_holes, -- herfa biçûk
["hûr\1tr"] = list_allow_holes,
["hanja"] = list_allow_holes, -- hanja
["hanja\1tr"] = list_allow_holes,
})
end
function export.cure(frame)
local m_headword = require("Module:headword")
local function track(page)
require("Module:debug/track")("headword/templates/" .. page)
return true
end
local args = get_args(frame)
local lang = args[1]
local kod = lang:getCode()
local ziman = lang:getCanonicalName()
-- Get language and script information
local data = {}
local sc = args["sc"]
local sccode
if sc then
sccode = sc:getCode()
end
data.sccat = args["sckat"]
data.lang = lang
data.sc = sc
data.heads = args[2] or args["cuda"]
data.id = args["id"]
data.translits = args["tr"]
data.transcriptions = args["ts"]
data.gloss = args["gloss"]
local args_c = args["c"]
-- This shouldn't really happen.
for i = 1,args["cuda"].maxindex do
if not args["cuda"][i] then
track("head-with-holes")
end
end
-- Part-of-speech category
local cureye_rezimani = frame.args["nav"]
data.noposcat = args["nekatcure"]
local bike_form = args["form"] --form=erê binivîse ji bo ku formên navdêran lêkeran hwd. be
if bike_form == "erê" then
cureye_rezimani = "formên " .. cureye_rezimani .. "an"
end
-- Check for headword aliases and then pluralize if the POS term does not have an invariable plural.
data.pos_category = cureye_rezimani
-- Additional categories.
data.categories = {}
data.whole_page_categories = {}
data.nomultiwordcat = args["nekatçendterm"]
data.nogendercat = args["nekatcins"]
data.nopalindromecat = args["nekatpalîndrom"]
local pagename = mw.title.getCurrentTitle().text
-- Headword linking
data.nolinkhead = args["serlînkneke"]
if args["neformtr"] then
data.inflections = {enable_auto_translit = false}
else
data.inflections = {enable_auto_translit = true}
end
if bike_form == "-" and cureye_rezimani == "rengdêr" then
insert(data.categories, 'Rengdêrên neqiyasbar bi ' .. ziman )
insert(data.inflections, { label = ferhengok("neqiyasbar")})
end
-- EXPERIMENTAL: see [[:en:Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
local forceformaalt = {}
local ku_sc_arab
if kod == "ku" then
if sccode == "Arab" or sccode == "ku-Arab" then
ku_sc_arab = "erê"
else
local check_sc = lang:findBestScript(pagename)
if check_sc:getCode() == "ku-Arab" then
ku_sc_arab = "erê"
end
end
if ku_sc_arab == "erê" then
insert(forceformaalt, true)
insert(data.categories, "Peyvên " ..ziman .. " bi alfabeya erebî" )
data.altform = forceformaalt
else
data.altform = args["formaalt"]
end
else
data.altform = args["formaalt"]
end
-- Inflected forms
-- Assuming the parameters are named like "m1tr", "n1tr", etc.
local inflection_labels = {
["m"] = "forma mê",
["n"] = "forma nêr",
["nt"] = "forma nêtar",
["p"] = "forma pirjimar",
["y"] = "forma yekjimar",
["np"] = "forma nêr ya pirjimar",
["mp"] = "forma mê ya pirjimar",
["lk"] = "forma lêker",
["nd"] = "forma navdêr",
["gir"] = "girek",
["hûr"] = "hûrek",
["hanja"] = "hanja",
["sûp"] = "forma sûperlatîv",
["komp"] = "forma komparatîv",
["niha"] = "rehê dema niha",
["borî"] = "rehê dema borî",
["subj"] = "rehê dema bilanî",
["fniha"] = "forma dema niha",
["fborî"] = "forma dema borî",
["fsubj"] = "forma dema bilanî",
}
for code, label in pairs(inflection_labels) do
local values = args[code] -- This is a list (could have holes)
if values and values.maxindex > 0 then
local infl_part = { label = label }
-- Get the transliteration list for this code
local tr_list = args[code .. "tr"]
for i = 1, values.maxindex do
if values[i] and values[i] ~= "" then
-- Get the corresponding transliteration if available
local tr_value = tr_list and tr_list[i]
insert(infl_part, { term = values[i], translit = tr_value })
end
end
if #infl_part > 0 then
insert(data.inflections, infl_part)
end
end
end
local aspect_tab = {
['împf'] = 'împerfektîv',
['impf'] = 'împerfektîv',
['pf'] = 'perfektîv',
['gh'] = 'gerguhêz',
['ngh'] = 'negerguhêz',
}
local daristin_tab = {
['x'] = 'xwerû',
['p'] = 'pêkhatî',
['h'] = 'hevedudanî',
['b'] = 'biwêjî',
}
local pos_for_gender_number_cat = {
["navdêr"] = "navdêrên",
["serenav"] = "serenavên",
["paşgir"] = "paşgirên",
["cînav"] = "cînavên",
}
local cureyen_lekere = {}
local cure = args_c[1]
if cureye_rezimani == "lêker" and cure and cure ~= '' then
local splitted_cure = mw.text.split(cure, '-')
local c1 = splitted_cure[1]
local c2 = splitted_cure[2]
if c2 and c2 ~= '' then
if daristin_tab[c1] and aspect_tab[c2] then
insert(data.inflections, 1, { label = "lêkera " .. ferhengok(daristin_tab[c1])})
insert(cureyen_lekere, c2)
if bike_form ~= "erê" and ku_sc_arab ~= "erê" then
insert(data.categories, 'Lêkerên ' .. daristin_tab[c1] .. ' bi ' .. ziman)
insert(data.categories, 'Lêkerên ' .. daristin_tab[c1] .. ' yên ' .. aspect_tab[c2] .. ' bi ' .. ziman)
end
elseif aspect_tab[c1] and daristin_tab[c2] then
error("Invalid format: aspect code must come after leker code (e.g., x-gh, not gh-x)")
else
error("Invalid combination of codes in parameter c")
end
else
if daristin_tab[c1] then
insert(data.inflections, { label = "lêkera " .. ferhengok(daristin_tab[c1])})
elseif aspect_tab[c1] then
insert(cureyen_lekere, c1)
if bike_form ~= "erê" and ku_sc_arab ~= "erê" then
insert(data.categories, 'Lêkerên ' .. aspect_tab[c1] .. ' bi ' .. ziman)
end
elseif c1 == '?' and bike_form ~= "erê" then
insert(cureyen_lekere, "asp?")
else
error("Invalid code in cure parameter")
end
end
if #cureyen_lekere > 0 then
data.genders = cureyen_lekere
end
elseif pos_for_gender_number_cat[cureye_rezimani] and ((cure and cure ~= '') or args["j"][1]) then
local cins = args["c"]
local jimar = args["j"]
-- Only process cins if it exists and isn't empty
if cins and #cins > 0 then
-- Remove "-" values from cins
local i = 1
while i <= #cins do
if cins[i] == "-" then
table.remove(cins, i)
elseif cins[i] == "mn" and kod == "ku" then
cins[i] = "mnmane"
i = i + 1
else
i = i + 1
end
end
else
cins = {} -- Initialize as empty table if nil
end
local genders = {}
-- Pair corresponding cins and jimar values
local min_length = math.min(#cins, #jimar)
for i = 1, min_length do
insert(genders, cins[i] .. "-" .. jimar[i])
end
-- Add any remaining cins values
for i = min_length + 1, #cins do
insert(genders, cins[i])
end
-- Add any remaining jimar values
for i = min_length + 1, #jimar do
insert(genders, jimar[i])
end
if #genders > 0 then
data.genders = genders
end
elseif kod == 'ku' and (type(args_c) ~= 'table' or #args_c == 0 or args_c[1] == '' or args_c[1] == '?') and form ~= 'erê' then
local cureyen_lekere = {}
if cureye_rezimani == 'lêker' and ku_sc_arab ~= "erê" then
insert(cureyen_lekere, "asp?") --('Lêkerên bêcure ('.. ziman .. ')')
elseif cureye_rezimani == 'navdêr' and ku_sc_arab ~= "erê" then
insert(cureyen_lekere, "c?") --('Lêkerên bêcure ('.. ziman .. ')')
end
data.genders = cureyen_lekere
else
data.genders = args["c"]
end
for k, v in pairs(args["cetîk"]) do
if k ~= "maxindex" then
if data.genders[k] then
data.genders[k] = {spec = data.genders[k], qualifiers = {v}}
else
k = k == 1 and "" or tostring(k)
error(("c%setîk= specified without c%s="):format(k, k))
end
end
end
for k, v in pairs(args["cref"]) do
if k ~= "maxindex" then
if data.genders[k] then
data.genders[k] = {spec = data.genders[k], refs = {v}}
else
k = k == 1 and "" or tostring(k)
error(("c%sref= specified without c%s="):format(k, k))
end
end
end
-- Lêkerên kurmancî li gor paşgirê.
valahiheye = pagename:find(" ", nil, true)
if (kod == 'ku' and cureye_rezimani == 'lêker' and bike_form ~= 'erê' and ku_sc_arab ~= "erê" and not valahiheye) then
suffix_verb = {'în', 'andin', 'endin', 'tin', 'in', 'an', 'ûn', 'n'}
if cureye_lekere then
cure_ku = mw.text.split(cureye_lekere, '-')
else
cure_ku = ''
end
for i, suffix in ipairs(suffix_verb) do
if mw.ustring.find(pagename, suffix .. '$') then
if mw.ustring.find(pagename, ' ') then
insert(data.categories, 'Lêkerên hevedudanî yên ' .. ziman .. ' bi paşgira -' .. suffix)
elseif cure_ku[1] == 'p' or cure_ku[2] == 'p' then
insert(data.categories, 'Lêkerên pêkhatî yên ' .. ziman .. ' bi paşgira -' .. suffix)
else
insert(data.categories, 'Lêkerên ' .. ziman .. ' bi paşgira -' .. suffix)
end
break
elseif i == #suffix_verb then
insert(data.categories, 'Lêkerên ' .. ziman .. ' bi çewtiya paşgirê')
end
end
end
return m_headword.full_headword(data)
end
return export
s3u6eoiwj69xwuerrtl4jzo7z9vhu29
6334243
6334115
2026-08-16T21:46:00Z
Wikihez
7344
bnr. [[Special:PermanentLink/6334242#c-Ghybu-20260816210600-Wikihez-20260816205500|nîqaşê]]
6334243
Scribunto
text/plain
insert = table.insert
local process_params = require("Module:parameters").process
local export = {}
local function ferhengok(entry, text)
text = text or entry
return "[[Nimînok:Ferhengok#" .. entry .. "|" .. text .. "]]"
end
local function get_args(frame)
local boolean = {type = "boolean"}
local boolean_list_allow_holes = {type = "boolean", list = true, allow_holes = true}
local list_allow_holes = {list = true, allow_holes = true}
return process_params(frame:getParent().args, {
[1] = {required = true, type = "language", default = "und"},
["sc"] = {type = "script"},
["sort"] = true,
["sckat"] = boolean,--sccat
["sccat"] = {boolean, alias_of = "sckat"},
["nekatcure"] = boolean,--noposcat
["noposcat"] = {boolean, alias_of = "nekatcure"},
["nekatçendterm"] = boolean,--nomultiwordcat
["nomultiwordcat"] = {boolean, alias_of = "nekatçendterm"},
["nekatcins"] = boolean,--nogendercat
["nogendercat"] = {boolean, alias_of = "nekatcins"},
["nekatpalîndrom"] = boolean,--nopalindromecat
["nopalindromecat"] = {boolean, alias_of = "nekatpalîndrom"},
["serlînkneke"] = boolean,--nolinkhead
["nolinkhead"] = {boolean, alias_of = "serlînkneke"},
["neformtr"] = boolean,
["form"] = true,
["formaalt"] = boolean, --altform EXPERIMENTAL: see [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
[2] = list_allow_holes,
["cuda"] = list_allow_holes,
["id"] = true,
["tr"] = list_allow_holes,
["ts"] = list_allow_holes,
["gloss"] = true, -- bo çi ye? En.wikt bi kar neyîne
["c"] = {list = true}, --ji bo lêkeran cure, ji bo navdêran cins(iyet)
["c\1etîk"] = list_allow_holes, --etîketek ji bo cins(iyet)
["c\1ref"] = list_allow_holes,--çavkanî ji bo cins(iyet)
["z"] = {list = true, alias_of = "c"},
["z\1qual"] = {list = true, alias_of = "c\1etîk"},
["z\1ref"] = {list = true, alias_of = "c\1ref"},
["j"] = {list = true}, -- jimar
["m"] = list_allow_holes,
["m\1tr"] = list_allow_holes,
["n"] = list_allow_holes,
["n\1tr"] = list_allow_holes,
["nt"] = list_allow_holes,
["nt\1tr"] = list_allow_holes,
["p"] = list_allow_holes,
["p\1tr"] = list_allow_holes,
["y"] = list_allow_holes,
["y\1tr"] = list_allow_holes,
["np"] = list_allow_holes,-- forma nêr ya pirjimar
["np\1tr"] = list_allow_holes,
["mp"] = list_allow_holes,-- forma mê ya pirjimar
["mp\1tr"] = list_allow_holes,
["lk"] = list_allow_holes, --forma lêker
["nd"] = list_allow_holes, --forma navdêr
["niha"] = list_allow_holes, -- rehê dema niha
["niha\1tr"] = list_allow_holes,
["borî"] = list_allow_holes, -- rehê dema borî
["borî\1tr"] = list_allow_holes,
["subj"] = list_allow_holes, -- rehê dema bilanî
["subj\1tr"] = list_allow_holes,
["fniha"] = list_allow_holes, -- forma dema niha
["fniha\1tr"] = list_allow_holes,
["fborî"] = list_allow_holes, -- forma dema borî
["fborî\1tr"] = list_allow_holes,
["fsubj"] = list_allow_holes, -- forma dema fermanî
["fsubj\1tr"] = list_allow_holes,
["fdbê"] = list_allow_holes, -- forma dema bê
["fdbê\1tr"] = list_allow_holes,
["sûp"] = list_allow_holes, -- forma sûperlatîv
["sûp\1tr"] = list_allow_holes,
["komp"] = list_allow_holes, -- forma komparatîv
["komp\1tr"] = list_allow_holes,
["gir"] = list_allow_holes, -- herfa mezin
["gir\1tr"] = list_allow_holes,
["hûr"] = list_allow_holes, -- herfa biçûk
["hûr\1tr"] = list_allow_holes,
["hanja"] = list_allow_holes, -- hanja
["hanja\1tr"] = list_allow_holes,
})
end
function export.cure(frame)
local m_headword = require("Module:headword")
local function track(page)
require("Module:debug/track")("headword/templates/" .. page)
return true
end
local args = get_args(frame)
local lang = args[1]
local kod = lang:getCode()
local ziman = lang:getCanonicalName()
-- Get language and script information
local data = {}
local sc = args["sc"]
local sccode
if sc then
sccode = sc:getCode()
end
data.sccat = args["sckat"]
data.lang = lang
data.sc = sc
data.heads = args[2] or args["cuda"]
data.id = args["id"]
data.translits = args["tr"]
data.transcriptions = args["ts"]
data.gloss = args["gloss"]
local args_c = args["c"]
-- This shouldn't really happen.
for i = 1,args["cuda"].maxindex do
if not args["cuda"][i] then
track("head-with-holes")
end
end
-- Part-of-speech category
local cureye_rezimani = frame.args["nav"]
data.noposcat = args["nekatcure"]
local bike_form = args["form"] --form=erê binivîse ji bo ku formên navdêran lêkeran hwd. be
if bike_form == "erê" then
cureye_rezimani = "formên " .. cureye_rezimani .. "an"
end
-- Check for headword aliases and then pluralize if the POS term does not have an invariable plural.
data.pos_category = cureye_rezimani
-- Additional categories.
data.categories = {}
data.whole_page_categories = {}
data.nomultiwordcat = args["nekatçendterm"]
data.nogendercat = args["nekatcins"]
data.nopalindromecat = args["nekatpalîndrom"]
local pagename = mw.title.getCurrentTitle().text
-- Headword linking
data.nolinkhead = args["serlînkneke"]
if args["neformtr"] then
data.inflections = {enable_auto_translit = false}
else
data.inflections = {enable_auto_translit = true}
end
if bike_form == "-" and cureye_rezimani == "rengdêr" then
insert(data.categories, 'Rengdêrên neqiyasbar bi ' .. ziman )
insert(data.inflections, { label = ferhengok("neqiyasbar")})
end
-- EXPERIMENTAL: see [[:en:Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]]
local forceformaalt = {}
local ku_sc_arab
if kod == "ku" then
if sccode == "Arab" or sccode == "ku-Arab" then
ku_sc_arab = "erê"
else
local check_sc = lang:findBestScript(pagename)
if check_sc:getCode() == "ku-Arab" then
ku_sc_arab = "erê"
end
end
if ku_sc_arab == "erê" then
insert(forceformaalt, true)
insert(data.categories, "Peyvên " ..ziman .. " bi alfabeya erebî" )
data.altform = forceformaalt
else
data.altform = args["formaalt"]
end
else
data.altform = args["formaalt"]
end
-- Inflected forms
-- Assuming the parameters are named like "m1tr", "n1tr", etc.
local inflection_labels = {
["m"] = "forma mê",
["n"] = "forma nêr",
["nt"] = "forma nêtar",
["p"] = "forma pirjimar",
["y"] = "forma yekjimar",
["np"] = "forma nêr ya pirjimar",
["mp"] = "forma mê ya pirjimar",
["lk"] = "forma lêker",
["nd"] = "forma navdêr",
["gir"] = "girek",
["hûr"] = "hûrek",
["hanja"] = "hanja",
["sûp"] = "forma sûperlatîv",
["komp"] = "forma komparatîv",
["niha"] = "rehê dema niha",
["borî"] = "rehê dema borî",
["subj"] = "rehê dema bilanî",
["fniha"] = "forma dema niha",
["fborî"] = "forma dema borî",
["fsubj"] = "forma dema fermanî",
["fdbê"] = "forma dema bê",
}
for code, label in pairs(inflection_labels) do
local values = args[code] -- This is a list (could have holes)
if values and values.maxindex > 0 then
local infl_part = { label = label }
-- Get the transliteration list for this code
local tr_list = args[code .. "tr"]
for i = 1, values.maxindex do
if values[i] and values[i] ~= "" then
-- Get the corresponding transliteration if available
local tr_value = tr_list and tr_list[i]
insert(infl_part, { term = values[i], translit = tr_value })
end
end
if #infl_part > 0 then
insert(data.inflections, infl_part)
end
end
end
local aspect_tab = {
['împf'] = 'împerfektîv',
['impf'] = 'împerfektîv',
['pf'] = 'perfektîv',
['gh'] = 'gerguhêz',
['ngh'] = 'negerguhêz',
}
local daristin_tab = {
['x'] = 'xwerû',
['p'] = 'pêkhatî',
['h'] = 'hevedudanî',
['b'] = 'biwêjî',
}
local pos_for_gender_number_cat = {
["navdêr"] = "navdêrên",
["serenav"] = "serenavên",
["paşgir"] = "paşgirên",
["cînav"] = "cînavên",
}
local cureyen_lekere = {}
local cure = args_c[1]
if cureye_rezimani == "lêker" and cure and cure ~= '' then
local splitted_cure = mw.text.split(cure, '-')
local c1 = splitted_cure[1]
local c2 = splitted_cure[2]
if c2 and c2 ~= '' then
if daristin_tab[c1] and aspect_tab[c2] then
insert(data.inflections, 1, { label = "lêkera " .. ferhengok(daristin_tab[c1])})
insert(cureyen_lekere, c2)
if bike_form ~= "erê" and ku_sc_arab ~= "erê" then
insert(data.categories, 'Lêkerên ' .. daristin_tab[c1] .. ' bi ' .. ziman)
insert(data.categories, 'Lêkerên ' .. daristin_tab[c1] .. ' yên ' .. aspect_tab[c2] .. ' bi ' .. ziman)
end
elseif aspect_tab[c1] and daristin_tab[c2] then
error("Invalid format: aspect code must come after leker code (e.g., x-gh, not gh-x)")
else
error("Invalid combination of codes in parameter c")
end
else
if daristin_tab[c1] then
insert(data.inflections, { label = "lêkera " .. ferhengok(daristin_tab[c1])})
elseif aspect_tab[c1] then
insert(cureyen_lekere, c1)
if bike_form ~= "erê" and ku_sc_arab ~= "erê" then
insert(data.categories, 'Lêkerên ' .. aspect_tab[c1] .. ' bi ' .. ziman)
end
elseif c1 == '?' and bike_form ~= "erê" then
insert(cureyen_lekere, "asp?")
else
error("Invalid code in cure parameter")
end
end
if #cureyen_lekere > 0 then
data.genders = cureyen_lekere
end
elseif pos_for_gender_number_cat[cureye_rezimani] and ((cure and cure ~= '') or args["j"][1]) then
local cins = args["c"]
local jimar = args["j"]
-- Only process cins if it exists and isn't empty
if cins and #cins > 0 then
-- Remove "-" values from cins
local i = 1
while i <= #cins do
if cins[i] == "-" then
table.remove(cins, i)
elseif cins[i] == "mn" and kod == "ku" then
cins[i] = "mnmane"
i = i + 1
else
i = i + 1
end
end
else
cins = {} -- Initialize as empty table if nil
end
local genders = {}
-- Pair corresponding cins and jimar values
local min_length = math.min(#cins, #jimar)
for i = 1, min_length do
insert(genders, cins[i] .. "-" .. jimar[i])
end
-- Add any remaining cins values
for i = min_length + 1, #cins do
insert(genders, cins[i])
end
-- Add any remaining jimar values
for i = min_length + 1, #jimar do
insert(genders, jimar[i])
end
if #genders > 0 then
data.genders = genders
end
elseif kod == 'ku' and (type(args_c) ~= 'table' or #args_c == 0 or args_c[1] == '' or args_c[1] == '?') and form ~= 'erê' then
local cureyen_lekere = {}
if cureye_rezimani == 'lêker' and ku_sc_arab ~= "erê" then
insert(cureyen_lekere, "asp?") --('Lêkerên bêcure ('.. ziman .. ')')
elseif cureye_rezimani == 'navdêr' and ku_sc_arab ~= "erê" then
insert(cureyen_lekere, "c?") --('Lêkerên bêcure ('.. ziman .. ')')
end
data.genders = cureyen_lekere
else
data.genders = args["c"]
end
for k, v in pairs(args["cetîk"]) do
if k ~= "maxindex" then
if data.genders[k] then
data.genders[k] = {spec = data.genders[k], qualifiers = {v}}
else
k = k == 1 and "" or tostring(k)
error(("c%setîk= specified without c%s="):format(k, k))
end
end
end
for k, v in pairs(args["cref"]) do
if k ~= "maxindex" then
if data.genders[k] then
data.genders[k] = {spec = data.genders[k], refs = {v}}
else
k = k == 1 and "" or tostring(k)
error(("c%sref= specified without c%s="):format(k, k))
end
end
end
-- Lêkerên kurmancî li gor paşgirê.
valahiheye = pagename:find(" ", nil, true)
if (kod == 'ku' and cureye_rezimani == 'lêker' and bike_form ~= 'erê' and ku_sc_arab ~= "erê" and not valahiheye) then
suffix_verb = {'în', 'andin', 'endin', 'tin', 'in', 'an', 'ûn', 'n'}
if cureye_lekere then
cure_ku = mw.text.split(cureye_lekere, '-')
else
cure_ku = ''
end
for i, suffix in ipairs(suffix_verb) do
if mw.ustring.find(pagename, suffix .. '$') then
if mw.ustring.find(pagename, ' ') then
insert(data.categories, 'Lêkerên hevedudanî yên ' .. ziman .. ' bi paşgira -' .. suffix)
elseif cure_ku[1] == 'p' or cure_ku[2] == 'p' then
insert(data.categories, 'Lêkerên pêkhatî yên ' .. ziman .. ' bi paşgira -' .. suffix)
else
insert(data.categories, 'Lêkerên ' .. ziman .. ' bi paşgira -' .. suffix)
end
break
elseif i == #suffix_verb then
insert(data.categories, 'Lêkerên ' .. ziman .. ' bi çewtiya paşgirê')
end
end
end
return m_headword.full_headword(data)
end
return export
fkzg6kg9n5e2z7h5nzbdox555deejfe
kanistan
0
534478
6334265
6327462
2026-08-16T23:16:21Z
Wikihez
7344
[[Bikarhêner:Wikihez/templateScript2.js|TS]]: sererastkirin
6334265
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[cih|Cih]]ê ku tiştek jê hatiye yan jê çêbûye yan jî jê hatiye [[wergirtin]].
#: {{hevmane|ku|jêder|serekanî}}
#: {{nimûne|ku|Çavkaniya wê nûçeyê çi ye?|Ew xeber ji kû hatiye bîstin?|rêz=1}}
==== Hevmane ====
* {{g|ku|jêder}}
* {{g|ku|serekanî}}
==== Etîmolojî ====
Ji {{qertaf|ku|çav|kanî}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|serçawe}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W|af|bron}}
* {{Z|sq}}: {{W|sq|krua}}
* {{Z|de}}: {{W|de|Ursprung}} {{n}}, {{W|de|Herkunft}} {{mê}}, {{W|de|Bibliographie}} {{mê}}, {{W|de|Quelle}} {{mê}}, {{W|de|Wasserquelle}}, {{W|de|Vorrat|guman=erê}} {{n}}
* {{Z|da}}: {{W|da|kilde}}
* {{Z|ar}}: {{W|ar|مصدر}}, {{W|ar|منبع}}, {{W|ar|منشأ}}, {{W|ar|أصل}}
* {{Z|eo}}: {{W|eo|fonto}}
* {{Z|fa}}: {{W|fa|چشمه}}, {{W|fa|زاب}}, {{W|fa|سرچشمه}}, {{W|fa|مأخذ}}, {{W|fa|منابع}}
* {{Z|fo}}: {{W|fo|kelda}}
* {{Z|fi}}: {{W|fi|lähde}}
* {{Z|fr}}: {{W|fr|origin}}
* {{Z|nl}}: {{W|nl|bron}}
* {{Z|he}}: {{W|he|מעין|cuda=מַעְיָן}}, {{W|he|ימם|cuda=יֵמִם}}
* {{Z|en}}: {{W|en|source}}, {{W|en|origin}}, {{W|en|fountain}}, {{W|en|resource}}, {{W|en|wellspring}}
* {{Z|it}}: {{W|it|fonte}}, {{W|it|sorgente}}
* {{Z|is}}: {{W|is|brunnur}}
* {{Z|ca}}: {{W|ca|origen}}
* {{Z|la}}: {{W|la|matrix}}, {{W|la|stirps}}, {{W|la|fons}}
* {{Z|ms}}: {{W|ms|mata-air}}
* {{Z|yua}}: {{W|yua|sayab}}, {{W|yua|sayab ha’}}
* {{Z|no}}: {{W|no|kilde}}
* {{Z|pap}}: {{W|pap|fuente}}
* {{Z|pt}}: {{W|pt|orgime}}, {{W|pt|fonte}}
* {{Z|ru}}: {{W|ru|источник}}
* {{Z|es}}: {{W|es|origen}}
* {{Z|sw}}: {{W|sw|kisima}}
* {{Z|sv}}: {{W|sv|källa}}
* {{Z|tr}}: {{W|tr|kaynak}}, {{W|tr|eşme}}, {{W|tr|göze}}, {{W|tr|memba}}, {{W|tr|orijin}}, {{W|tr|pınar}}, {{W|tr|referans}}, {{W|tr|biblografya}}, {{W|tr|göz}}, {{W|tr|mahreç}}, {{W|tr|suyun başı}}, {{W|tr|kaynakça}}
{{werger-bin}}
c5k43ry6vng4pm7yi35patkb8lvqm7b
6334266
6334265
2026-08-16T23:21:50Z
Wikihez
7344
/* Navdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334266
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[cih|Cih]]ê ku tiştek jê hatiye yan jê çêbûye yan jî jê hatiye [[wergirtin]].
#: {{hevmane|ku|jêder|serekanî}}
==== Hevmane ====
* {{g|ku|jêder}}
* {{g|ku|serekanî}}
==== Etîmolojî ====
Ji {{qertaf|ku|kanî|-istan<cuda:-stan>}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|serçawe}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W|af|bron}}
* {{Z|sq}}: {{W|sq|krua}}
* {{Z|de}}: {{W|de|Ursprung}} {{n}}, {{W|de|Herkunft}} {{mê}}, {{W|de|Bibliographie}} {{mê}}, {{W|de|Quelle}} {{mê}}, {{W|de|Wasserquelle}}, {{W|de|Vorrat|guman=erê}} {{n}}
* {{Z|da}}: {{W|da|kilde}}
* {{Z|ar}}: {{W|ar|مصدر}}, {{W|ar|منبع}}, {{W|ar|منشأ}}, {{W|ar|أصل}}
* {{Z|eo}}: {{W|eo|fonto}}
* {{Z|fa}}: {{W|fa|چشمه}}, {{W|fa|زاب}}, {{W|fa|سرچشمه}}, {{W|fa|مأخذ}}, {{W|fa|منابع}}
* {{Z|fo}}: {{W|fo|kelda}}
* {{Z|fi}}: {{W|fi|lähde}}
* {{Z|fr}}: {{W|fr|origin}}
* {{Z|nl}}: {{W|nl|bron}}
* {{Z|he}}: {{W|he|מעין|cuda=מַעְיָן}}, {{W|he|ימם|cuda=יֵמִם}}
* {{Z|en}}: {{W|en|source}}, {{W|en|origin}}, {{W|en|fountain}}, {{W|en|resource}}, {{W|en|wellspring}}
* {{Z|it}}: {{W|it|fonte}}, {{W|it|sorgente}}
* {{Z|is}}: {{W|is|brunnur}}
* {{Z|ca}}: {{W|ca|origen}}
* {{Z|la}}: {{W|la|matrix}}, {{W|la|stirps}}, {{W|la|fons}}
* {{Z|ms}}: {{W|ms|mata-air}}
* {{Z|yua}}: {{W|yua|sayab}}, {{W|yua|sayab ha’}}
* {{Z|no}}: {{W|no|kilde}}
* {{Z|pap}}: {{W|pap|fuente}}
* {{Z|pt}}: {{W|pt|orgime}}, {{W|pt|fonte}}
* {{Z|ru}}: {{W|ru|источник}}
* {{Z|es}}: {{W|es|origen}}
* {{Z|sw}}: {{W|sw|kisima}}
* {{Z|sv}}: {{W|sv|källa}}
* {{Z|tr}}: {{W|tr|kaynak}}, {{W|tr|eşme}}, {{W|tr|göze}}, {{W|tr|memba}}, {{W|tr|orijin}}, {{W|tr|pınar}}, {{W|tr|referans}}, {{W|tr|biblografya}}, {{W|tr|göz}}, {{W|tr|mahreç}}, {{W|tr|suyun başı}}, {{W|tr|kaynakça}}
{{werger-bin}}
i8xdbn4afiget9ac8eqyeoa2cxtdhju
6334282
6334266
2026-08-17T01:12:10Z
Balyozbot
7867
Bot: Paqijiyên kozmetîk (Lînk paqij kir.)
6334282
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# [[cih|Cihê]] ku tiştek jê hatiye yan jê çêbûye yan jî jê hatiye [[wergirtin]].
#: {{hevmane|ku|jêder|serekanî}}
==== Hevmane ====
* {{g|ku|jêder}}
* {{g|ku|serekanî}}
==== Etîmolojî ====
Ji {{qertaf|ku|kanî|-istan<cuda:-stan>}}.
==== Bi zaravayên din ====
* {{Z|ckb}}: {{W-|ckb|serçawe}}
==== Werger ====
{{werger-ser}}
* {{Z|af}}: {{W|af|bron}}
* {{Z|sq}}: {{W|sq|krua}}
* {{Z|de}}: {{W|de|Ursprung}} {{n}}, {{W|de|Herkunft}} {{mê}}, {{W|de|Bibliographie}} {{mê}}, {{W|de|Quelle}} {{mê}}, {{W|de|Wasserquelle}}, {{W|de|Vorrat|guman=erê}} {{n}}
* {{Z|da}}: {{W|da|kilde}}
* {{Z|ar}}: {{W|ar|مصدر}}, {{W|ar|منبع}}, {{W|ar|منشأ}}, {{W|ar|أصل}}
* {{Z|eo}}: {{W|eo|fonto}}
* {{Z|fa}}: {{W|fa|چشمه}}, {{W|fa|زاب}}, {{W|fa|سرچشمه}}, {{W|fa|مأخذ}}, {{W|fa|منابع}}
* {{Z|fo}}: {{W|fo|kelda}}
* {{Z|fi}}: {{W|fi|lähde}}
* {{Z|fr}}: {{W|fr|origin}}
* {{Z|nl}}: {{W|nl|bron}}
* {{Z|he}}: {{W|he|מעין|cuda=מַעְיָן}}, {{W|he|ימם|cuda=יֵמִם}}
* {{Z|en}}: {{W|en|source}}, {{W|en|origin}}, {{W|en|fountain}}, {{W|en|resource}}, {{W|en|wellspring}}
* {{Z|it}}: {{W|it|fonte}}, {{W|it|sorgente}}
* {{Z|is}}: {{W|is|brunnur}}
* {{Z|ca}}: {{W|ca|origen}}
* {{Z|la}}: {{W|la|matrix}}, {{W|la|stirps}}, {{W|la|fons}}
* {{Z|ms}}: {{W|ms|mata-air}}
* {{Z|yua}}: {{W|yua|sayab}}, {{W|yua|sayab ha’}}
* {{Z|no}}: {{W|no|kilde}}
* {{Z|pap}}: {{W|pap|fuente}}
* {{Z|pt}}: {{W|pt|orgime}}, {{W|pt|fonte}}
* {{Z|ru}}: {{W|ru|источник}}
* {{Z|es}}: {{W|es|origen}}
* {{Z|sw}}: {{W|sw|kisima}}
* {{Z|sv}}: {{W|sv|källa}}
* {{Z|tr}}: {{W|tr|kaynak}}, {{W|tr|eşme}}, {{W|tr|göze}}, {{W|tr|memba}}, {{W|tr|orijin}}, {{W|tr|pınar}}, {{W|tr|referans}}, {{W|tr|biblografya}}, {{W|tr|göz}}, {{W|tr|mahreç}}, {{W|tr|suyun başı}}, {{W|tr|kaynakça}}
{{werger-bin}}
os266y3hz9fa1gu5xluqt9d8tjiwo65
Modul:dara kategoriyan/ferhengokkatbike/dane/daxwaz
828
879732
6334144
6329403
2026-08-16T17:23:58Z
Wikihez
7344
6334144
Scribunto
text/plain
local labels = {}
labels["gotarên bi şaşî û kêmasî"] = {
description = "Madeyên bi zimanê {{{langname}}} yên ku tê de xetayên ku divê bên sererastkirin hene",
umbrella = true,
parents = {{sort = "!", template = "zimankatbike"}},
}
labels["madeyên zimanekî xelet bi kar tînin"] = {
description = "Madeyên ku tê de sinifa rêzimanî bi {{{langname}}} hatiye bikaranîn feqat di beşa zimanekî din de.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="!xelet"}},
--isHidden = fals, bila her daîm vekirî be da ku em bi hêsanî bibînin
}
labels["parametreya ser a nehewce"] = {
description = "Madeyên {{{langname}}} yên ku tê de şablonên rêzimanî bi parametreya |2= tê bikaranîn feqat ne hewce ye.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="!xelet"}},
--isHidden = fals, bila her daîm vekirî be da ku em bi hêsanî bibînin
}
labels["daxwazên jêgirtinan"] = {
description = "Daxwazên ji bo lêzêdekirina jêgirtinan bi zimanê {{{langname}}}.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="jêgirtin"}},
isHidden = true,
}
labels["daxwazên tarîxê"] = {
description = "Daxwazên ji bo lêzêdekirina tarîxa jêgirtinê di madeyên {{{langname}}} de.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="tarîx"}},
isHidden = true,
}
labels["daxwazên etîmolojiyê"] = {
description = "Madeyên {{{langname}}} yên ku etîmolojiya wê kêm e.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="etîmolojî"}, {name="etîmolojî", sort="!"}},
}
labels["daxwazên berfirehkirina etîmolojiyê"] = {
description = "Madeyên {{{langname}}} yên ku etîmolojiya wê zêde kurt e û divê were berfirehkirin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="etîmolojî"}, {name="etîmolojî", sort="!"}},
}
labels["lînkên bi parametreyên hedefê yên nehewce"] = {
description = "Lînkên bi parametreyên hedefê yên nehewce bi zimanê {{{langname}}}.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="lînk"}},
isHidden = true,
}
labels["lînkên ku parametreya cuda nehewce ye"] = {
description = "Lînkên {{{langname}}} yên ku parametreya {{param|cuda}} bi kar tînin lê ev parametre ne hewce ye.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="lînk"}},
isHidden = true,
}
labels["lînkên bi wîkîlînkên nehewce"] = {
description = "Lînkên {{{langname}}} yên ku tê de wîkîlînk hene lê ev ne hewce ye.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="lînk"}},
isHidden = true,
}
labels["madeyên bê beşa mane"] = {
description = "Madeyên bi {{{langname}}} yên ku tê de mane nehatiye nivîsin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="mane"}},
}
labels["gotarên bê kategoriya rêzimanî"] = {
description = "Madeyên bi {{{langname}}} yên ku tê de cureyê rêzimanî nehatiye nivîsin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="rêziman"}},
}
labels["daxwazên cinsê navdêran"] = {
description = "Madeyên {{{langname}}} yên ku tê de cinsê kelîmeyê nehatiye nivîsin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="cins"}, {name="navdêr", sort="cins"}},
}
labels["daxwazên cureyê lêkeran"] = {
description = "Madeyên {{{langname}}} yên ku tê de cureyê lêkerê nehatiye nivîsin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="cure"}, {name="lêker", sort="cure"}},
}
labels["daxwazên peyvan"] = {
description = "Peyvên {{{langname}}} yên ku hatine behskirin û tên xwestin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="peyv"}},
isHidden = true,
}
labels["daxwazên wergera jêgirtinan"] = {
description = "Gotarên {{{langname}}} yên ku tê de jêgirtin heye lê belê wergera kurdiya kurmancî tine ye.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="werger"}},
isHidden = true,
}
labels["daxwazên wergerandina nimûneyan"] = {
description = "Madeyên {{{langname}}} yên ku tê de nimûneya bikaranînê heye lê belê wergera kurmancî tine ye.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="wergerandin"}},
isHidden = true,
}
labels["daxwazên kontrolkirina rûpela wergerê"] = {
description = "Madeyên {{{langname}}} yên ku ji wergerekê hatine çêkirin û divê werin kontrolkirin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="kontrolkirin"}},
isHidden = true,
}
labels["daxwazên skrîpta peyvê"] = {
description = "Peyvên {{{langname}}} yên ku hatine behskirin lê belê têne transkrîptsiyona wan hene.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="skrîpt"}},
isHidden = true,
}
labels["transliterasyona nepêwîst"] = {
description = "Peyvên {{{langname}}} yên ku hatine transliterekirin lê belê ev transliterasyon ne pêwîst e, divê transliterasyon were jêbirin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="transliterasyon"}},
isHidden = true,
}
labels["transliterasyona zarûrî û manûel"] = {
description = "Peyvên {{{langname}}} yên ku manûel hatine transliterekirin û zarûrî ne, nikarin bi otomatîkî werin çêkirin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="transliterasyon"}},
isHidden = true,
}
labels["skrîpta nestandard"] = {
description = "Peyvên {{{langname}}} yên ku skrîpteke nestandard bi kar tînin.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="skrîpt"}},
isHidden = true,
}
labels["koda skrîptê ya nehewce"] = {
description = "Lînkên {{{langname}}} yên ku parametreya {{param|sc}} bi kar tînin lê nehewce ne.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="lînk"}},
isHidden = true,
}
labels["koda skrîptê ya zarûrî û manûel"] = {
description = "Lînkên {{{langname}}} yên ku parametreya {{param|sc}} manûel bi kar tînin û zarûrî ne.",
parents = {{name="gotarên bi şaşî û kêmasî", sort="lînk"}},
isHidden = true,
}
labels["jêgirtinên kitêban"] = {
description = "",
parents = {{name="jêgirtin", sort="kitêban"}},
isHidden = true,
}
labels["jêgirtinên malperan"] = {
description = "",
parents = {{name="jêgirtin", sort="malper"}},
isHidden = true,
}
labels["jêgirtinên kovaran"] = {
description = "",
parents = {{name="jêgirtin", sort="kovaran"}},
isHidden = true,
}
return {LABELS = labels, HANDLERS = handlers}
gviaotx4ds0m6za9sxc5yp6k85b086v
xanekirin
0
934971
6334141
6320205
2026-08-16T17:20:06Z
Wikihez
7344
/* Bi alfabeyên din */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334141
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|xanekirin|z=m|j=y}}
# xistin û rêzkirina tiştan ''weku pertûkan'' nav polan de, [[polandin]], [[polînkirin]], [[senifandin]]<ref>{{Fer:kur-ar:spîrêz|r=229}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Têkildar ====
* [[xane]]
* [[xane kirin]]
* [[xanî]]
==== Bi alfabeyên din ====
* {{ku-ar|خانەكرن}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W-|de|Einordnung}}, {{W+|de|Einteilung}}, {{W-|de|Einstufung}}, {{W+|de|Kategorisierung}},{{W+|de|Klassifizierung}}
* {{Z|ar}}: {{W-|ar|تصنيف}}, {{W-|ar|تبويب}}
* {{Z|fa}}: {{W-|fa|دسته بندى}}, {{W-|fa|طبقه بندى}}
* {{Z|en}}: {{W+|en|categorization}}, {{W+|en|classification}}, {{W+|en|sorting}}
* {{Z|tr}}: {{W-|tr|kategorizasiyon}}, {{W+|tr|sınıflandırma}}, {{W+|tr|tasnif}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
2vxto4e7c2x1a106pd66ii7a8w67zza
6334142
6334141
2026-08-16T17:21:06Z
Wikihez
7344
6334142
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# xistin û rêzkirina tiştan weku pertûkan nav polan de, [[polandin]], [[polînkirin]], [[senifandin]]<ref>{{Fer:kur-ar:spîrêz|r=229}}</ref>
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Bi alfabeyên din ====
* {{ku-ar|خانەكرن}}
==== Têkildar ====
* [[xane]]
* [[xane kirin]]
* [[xanî]]
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W-|de|Einordnung}}, {{W+|de|Einteilung}}, {{W-|de|Einstufung}}, {{W+|de|Kategorisierung}},{{W+|de|Klassifizierung}}
* {{Z|ar}}: {{W-|ar|تصنيف}}, {{W-|ar|تبويب}}
* {{Z|fa}}: {{W-|fa|دسته بندى}}, {{W-|fa|طبقه بندى}}
* {{Z|en}}: {{W+|en|categorization}}, {{W+|en|classification}}, {{W+|en|sorting}}
* {{Z|tr}}: {{W-|tr|kategorizasiyon}}, {{W+|tr|sınıflandırma}}, {{W+|tr|tasnif}}
{{werger-bin}}
=== Çavkanî ===
{{çavkanî}}
8pvzwie6df42a9xdl91ovtkcswnvpga
şîrfiroş
0
1226455
6334161
6323431
2026-08-16T17:45:20Z
Wikihez
7344
/* Etîmolojî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334161
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
[[Wêne:The milk delivery guy (3965278591).jpg|thumb|Şîrfiroşek şîr digihîne mişteriyan.]]
{{navdêr|ku|z=mn}}
# Kesê ku [[şîr]] [[firotin|difiroşe]]; kesê ku [[şîr]] [[gihandin|digihîne]] [[mal]]an yan [[dikan]]an.
#: {{hevmane|ku|şîrgêr|şîrvan}}
==== Tewandin ====
{{ku-tewîn-nav/nû|mê û nêr}}
==== Jê ====
* {{g|ku|şîrfiroşî}}
==== Etîmolojî ====
Ji {{qertaf|ku|şîr|-firoş}}.
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|Milchmann|n}}
* {{Z|rup}}: {{W-|rup|lãptar|n}}
* {{Z|ast}}: {{W-|ast|llecheru|n}}
* {{Z|be}}: {{W-|be|мало́чнік|n}}
* {{Z|bn}}: {{W-|bn|দুধওয়ালা|tr=dudhōẇala}}, {{W-|bn|গোয়ালা}}
* {{Z|bg}}: {{W-|bg|млека́р|n}}
* {{Z|cs}}: {{W+|cs|mlékař|n}}
* {{Z|zh}}:
*: {{Z|cmn}}: {{W-|cmn|送奶人|tr=sòngnǎirén}}
* {{Z|da}}: {{W-|da|mælkemand|g}}
* {{Z|ar}}: {{W-|ar|[[بَائِع]] [[لَبَن]]|n}}
* {{Z|et}}: {{W+|et|piimamees}}
* {{Z|fi}}: {{W+|fi|maitomies}}
* {{Z|fr}}: {{W+|fr|laitier|n}}, {{W+|fr|crémier|n}}
* {{Z|fur}}: {{W-|fur|latâr|n}}
* {{Z|gl}}: {{W-|gl|leiteiro|n}}
* {{Z|hi}}: {{W+|hi|दूधवाला|n}}
* {{Z|nl}}: {{W+|nl|melkboer|n}}
* {{Z|en}}: {{W+|en|milkman}}
* {{Z|ga}}: {{W-|ga|fear bainne|n}}
* {{Z|it}}: {{W+|it|lattaio|n}}
* {{Z|ja}}: {{W+|ja|牛乳屋|tr=ぎゅうにゅうや, gyūnyūya}}, {{W-|ja|[[牛乳]][[配達]]|tr=ぎゅにゅうはいたつ, gyūnyū haitatsu}}
* {{Z|ca}}: {{W-|ca|lleter|n}}
* {{Z|ky}}: {{W-|ky|сүтчү}}
* {{Z|ko}}: {{W-|ko|[[우유]] [[배달원]]}}
* {{Z|lb}}: {{W-|lb|Mëllechmann|n}}
* {{Z|mk}}: {{W-|mk|млекар|n}}
* {{Z|mt}}: {{W-|mt|tal-ħalib|n|m}}
* {{Z|hu}}: {{W+|hu|tejesember}}, {{W+|hu|tejes}}
* {{Z|no}}:
*: {{Z|nb}}: {{W-|nb|melkemann|n}}
* {{Z|oc}}: {{W-|oc|lachièr|n}}
* {{Z|pl}}: {{W+|pl|mleczarz|n}}
* {{Z|pt}}: {{W+|pt|leiteiro|n}}
* {{Z|ro}}: {{W+|ro|lăptar|n}}
* {{Z|ru}}: {{W+|ru|моло́чник|n}}, {{W-|ru|[[разно́счик]] [[молоко|молока́]]|n}}
* {{Z|scn}}: {{W-|scn|lattaru|n}}
* {{Z|sh}}:
*: Kirîlî: {{W-|sh|млѐка̄р|n}}, {{W-|sh|мљѐка̄р|n}}
*: Latînî: {{W+|sh|mlèkār|n}}, {{W+|sh|mljèkār|n}}
* {{Z|sk}}: {{W-|sk|mliekar|n}}
* {{Z|sl}}: {{W-|sl|mlekar|n}}
* {{Z|es}}: {{W+|es|lechero|n}}
* {{Z|sv}}: {{W-|sv|mjölkutkörare|g}}, {{W+|sv|mjölkbud|nt}}
* {{Z|tt}}: {{W-|tt|cөтче}}, {{W-|tt|сөт сатучы}}
* {{Z|tr}}: {{W+|tr|sütçü}}
* {{Z|ur}}: {{W-|ur|دُودْھ والا|n|tr=dūdh-vālā}}
* {{Z|uk}}: {{W-|uk|моло́чник|n}}
* {{Z|vec}}: {{W-|vec|latèr|n}}
* {{Z|vi}}: {{W-|vi|người bán sữa}}
* {{Z|cy}}: {{W-|cy|dyn llaeth|n}}, {{W-|cy|dyn llefrith|n}}
* {{Z|yi}}: {{W-|yi|מילכיקער}}
* {{Z|el}}: {{W+|el|γαλατάς|n}}
{{werger-bin}}
:{{werger çavkanî|en|milkman|beş=Ev qismê Wergerê|oldid=75071267}}
3js363hj63vc6jskp0sfuyr87ytda4p
ranêzîk
0
1320132
6334130
6316254
2026-08-16T17:13:54Z
Wikihez
7344
/* Jê */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334130
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Bilêvkirin ===
* {{ku-IPA}}
* {{ku-kîte}}
=== Navdêr ===
{{navdêr|ku|z=m}}
# {{dever|Serhed|Serheda Jorîn}} [[nêzîk]], [[berbirî]].
==== Tewandin ====
{{ku-tewîn-nav/nû|mê}}
==== Jê====
* [[ranêzîkbûn]]
* [[ranêzîk bûn]]
* [[ranêzîkkirin]]
* [[ranêzîk kirin]]
* [[ranêzîkatî]]
* [[ranêzîkatî kirin]]
==== Etîmolojî ====
{{ji?|ku}}
==== Werger ====
{{werger-ser}}
* {{Z|en}}: {{W+|en|close}}, {{W+|en|near}}, {{W+|en|nearby}}
* {{Z|ru}}: {{W+|ru|близко}}, {{W+|ru|поблизости}}
* {{Z|tr}}: {{W+|tr|yakın}}{{W+|tr|yan}}, {{W-|tr|yandaki}}
{{werger-bin}}
lkdwboaqtge6t8h3vk5x8oz86wu1gpa
qilqaldar
0
1327024
6334263
6327293
2026-08-16T23:06:58Z
Wikihez
7344
6334263
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Rengdêr ===
{{ku-tewîn-rd|qilqaldar}}
# {{mane?|ku}}
#* {{nimûne?|ku}}
=== Jê ===
*[[qilqilîn]]
*[[qilqilandin]]
==== Etîmolojî ====
{{bêetîmolojî}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|beklommen|guman=erê}}
* {{Z|da}}: {{W-|da|urolig}}
* {{Z|eo}}: {{W-|eo|anksia}}
* {{Z|fi}}: {{W+|fi|levoton}}, {{W+|fi|huolestunut}}
* {{Z|fr}}: {{W+|fr|anxieux}}
* {{Z|nl}}: {{W+|nl|angstig}}, {{W+|nl|bezorgd}}, {{W+|nl|ongerust}}
* {{Z|en}}: {{W+|en|anxious}}, {{W+|en|concerned}}, {{W+|en|loath}}
* {{Z|it}}: {{W+|it|ansioso}}
* {{Z|pt}}: {{W+|pt|ansioso}} {{n}}, {{W+|pt|ansiosa}} {{mê}}
* {{Z|sv}}: {{W+|sv|orolig}}
* {{Z|tr}}: {{W+|tr|endişeli}}, {{W+|tr|kaygılı}}, {{W+|tr|huzursuz}}, {{W+|tr|sıkıntılı}}, {{W+|tr|telaşlı}}
{{werger-bin}}
r0tm6c4kp130yoz65okwijhwkne4360
6334297
6334263
2026-08-17T10:04:54Z
Wikihez
7344
/* Jê */
6334297
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Rengdêr ===
{{ku-tewîn-rd|qilqaldar}}
# {{mane?|ku}}
#* {{nimûne?|ku}}
==== Jê ====
*[[qilqilîn]]
*[[qilqilandin]]
==== Etîmolojî ====
{{bêetîmolojî}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|beklommen|guman=erê}}
* {{Z|da}}: {{W-|da|urolig}}
* {{Z|eo}}: {{W-|eo|anksia}}
* {{Z|fi}}: {{W+|fi|levoton}}, {{W+|fi|huolestunut}}
* {{Z|fr}}: {{W+|fr|anxieux}}
* {{Z|nl}}: {{W+|nl|angstig}}, {{W+|nl|bezorgd}}, {{W+|nl|ongerust}}
* {{Z|en}}: {{W+|en|anxious}}, {{W+|en|concerned}}, {{W+|en|loath}}
* {{Z|it}}: {{W+|it|ansioso}}
* {{Z|pt}}: {{W+|pt|ansioso}} {{n}}, {{W+|pt|ansiosa}} {{mê}}
* {{Z|sv}}: {{W+|sv|orolig}}
* {{Z|tr}}: {{W+|tr|endişeli}}, {{W+|tr|kaygılı}}, {{W+|tr|huzursuz}}, {{W+|tr|sıkıntılı}}, {{W+|tr|telaşlı}}
{{werger-bin}}
ardp7zo4ft7jyzd3g4n024en3liwq3k
6334298
6334297
2026-08-17T10:16:05Z
Wikihez
7344
/* Rengdêr */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334298
wikitext
text/x-wiki
== {{ziman|ku}} ==
=== Rengdêr ===
{{ku-tewîn-rd|qilqaldar}}
{{rengdêr|ku}}
# {{mane?|ku}}
#* {{nimûne?|ku}}
==== Jê ====
*[[qilqilîn]]
*[[qilqilandin]]
==== Etîmolojî ====
{{bêetîmolojî}}
==== Werger ====
{{werger-ser}}
* {{Z|de}}: {{W+|de|beklommen|guman=erê}}
* {{Z|da}}: {{W-|da|urolig}}
* {{Z|eo}}: {{W-|eo|anksia}}
* {{Z|fi}}: {{W+|fi|levoton}}, {{W+|fi|huolestunut}}
* {{Z|fr}}: {{W+|fr|anxieux}}
* {{Z|nl}}: {{W+|nl|angstig}}, {{W+|nl|bezorgd}}, {{W+|nl|ongerust}}
* {{Z|en}}: {{W+|en|anxious}}, {{W+|en|concerned}}, {{W+|en|loath}}
* {{Z|it}}: {{W+|it|ansioso}}
* {{Z|pt}}: {{W+|pt|ansioso}} {{n}}, {{W+|pt|ansiosa}} {{mê}}
* {{Z|sv}}: {{W+|sv|orolig}}
* {{Z|tr}}: {{W+|tr|endişeli}}, {{W+|tr|kaygılı}}, {{W+|tr|huzursuz}}, {{W+|tr|sıkıntılı}}, {{W+|tr|telaşlı}}
{{werger-bin}}
dogb1llom8wiu3sda74kh92wpg4340b
Wîkîferheng:Dîwan/2026
4
1327120
6334102
6333563
2026-08-16T14:40:15Z
Keyomerd
8814
/* Bila formata en.wikt were bikaranîn */
6334102
wikitext
text/x-wiki
<noinclude>{{Dîwan/arşîv-ser|2026}}</noinclude>
{{clear}}
== Daxwazên standardkirinê ==
https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:Daxwaz%C3%AAn_standardkirin%C3%AA, Gelo di vê malperê de peyvên hatîye standart kirin; ji malperê rakin? Zêde tehlîhev e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:03, 8 kanûna paşîn 2026 (UTC)
:@[[Bikarhêner:Rewiye tine|Rewiye tine]] [[Taybet:Tercîh#mw-prefsection-gadgets]] li vir amûra [[MediaWiki:Gadget-aWa.js/belgekirin|awa]] aktîv bike û pê re dikarî gotûbêjan arşîv bikî. Li sayfa [[WF:DS]] li kêleka her sernavê gotûbêjê <nowiki>[arşîv bike]</nowiki> derdikeve, pê lê bike û ew ê arşîv bike. Eger nikarî tu tenê şablona {{ş|ds-kirî}} îlawe bike, ez ê dûre arşîv bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:35, 5 tîrmeh 2026 (UTC)
== Standartkirin ==
Sala borî, min pêşnîyazek kiribû. Halî hazir piştî daxwaza standarkirinê em du heftî disekinin, eger kesek fikrek dîyar neke. Raste rast peyv li gor ferhenga standart a mezopotamyayê tê standart kirin. Ji xwe sazîyek vê karî dike çima em vê barî dikin stuyê xwe. Mesela ez çaxe di wîkîferhengê de peyvek digêrim; standart a peyvê jî li cem xwe kontrol dikim. Eger destûr hebe bi germî dikarim wê malperê standart bikim. Benda bûna du heftîyan gelo ne zehmet e? Duyemîn pêşnîyaza min nîşandana standarta peyvê di malpera de ye. Kesek dema malperan binêre, divê li jora malperan bibîne ku ev peyv standart e. Ev jî hîzmet e. Çimkî nivîskar, wergêr, edîtor, rojnemavan hez dikin bi peyvê standart binivîsin. https://ku.wiktionary.org/wiki/k%C3%AAf li vir wekî mînak min daye nîşan. Lê îfade eger wisa be: Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r. 253) e yê çetir be. Çimkî îfadeya berê de peyva kurdî-tirkî derbas dibe û di mejîyê mirov de tevlihevî çêdibe.@[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Guherto|Guherto]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 10:25, 8 kanûna paşîn 2026 (UTC)
:Merheba. Hevalno dîyar e karê standarkirinê li wîkîferhengê rawestîya ye. Min sê peyv ji bo standartkirinê qeyd kir. Kes li ser şîrove nenivîsî. Mehek jî li ser derbas bû. Aniha ez dikarim van rupelan biguherim. Li gor qaîdeya hatine danîn wisa ye. Daxwaza min heta niha jî ew e kelîmeyên di ferhenga standartkirinê de heyî divê em bikaribin rasterast li vir jî bikin standart. An jî demê kurttir bikin. Li weqfa mezapotamyayê 20 mirov digêhin hev li ser peyvekî seetan nîqaş dikin. Ma hewceyê em jî li vir nîqaş bikin? Tevlî vê jî ji xwe kes li ser vê mijare nasekîne van dema. Xeynî vê pêşnîyaza min nîşandana standarta peyvê di malpera de hebû. Li ser malperê 'Standarda Rastnivîsîna Ferhenga Kurdî a Weqfa Mezopotamyayê kêf (r.253) e' Tiştek mîna wî hebe yê gelek kêrî her cure nivîskarê me were. Çimkî hin nivîskarên me hez dike bi kelîmeyên standart binivîse. Di malperê de bila vê hêsanî fam bike çêtir e. @[[Bikarhêner:Bikarhêner|Bikarhêner]]@[[Bikarhêner:Ghybu|Ghybu]] [[Bikarhêner:Rewiye tine|Rewiye tine]] ([[Gotûbêja bikarhêner:Rewiye tine|gotûbêj]]) 18:35, 17 sibat 2026 (UTC)
::Merheba @[[Bikarhêner:Rewiye tine|Rewiye tine]]. Ev format pir xirab dibe. Tu dikarî li beşa Çavkaniyê binivîsî bi şablona {{ş|kitêb}} binivîsî çêtir e û tê eynî maneyê. Lê her kes standarda Weqfa Mezopotamyayê qebûl nakin û ez jî şibhê dikim ku ev websîte bi awayekî kolektîv tê çêkirin an na. Loma em bi ya kitêba rastnivisînê çêkin û eger îtîraz tinebe, şablona {{ş|ds-kirî}} li gotûbêja [[WF:DS]] binivîse, dûre li gotûbêjê arşîv bike, ne hewce ye li hêviya kesên din bisekinî. Eger itirazek hebe, ê ku dixwaze dûre dîsa şablona ds zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:30, 5 tîrmeh 2026 (UTC)
:::[https://berwarnews.com/ferhenga-rastnivisine-qonaxeke-giring-a-standardkirina-kurmanci-ye/ Roportaja Mikail Bülbül] li ser proseya standardkirinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:57, 9 tîrmeh 2026 (UTC)
::::Min nû dît an jî nû hatiye zêdekirin lê meqaleyek bi navê [https://www.ferhengkurdi.com/?q=iltihap&modal=lijne lijneya edîtorîyê] nivîsandine [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:40, 22 tîrmeh 2026 (UTC)
== <span lang="en" dir="ltr">Annual review of the Universal Code of Conduct and Enforcement Guidelines</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 9 February 2026. This is the first step of several to be taken for the annual review. [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]])<section end="announcement-content" />
</div>
21:02, 19 kanûna paşîn 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 -->
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English. {{int:Please-translate}}
A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[Bikarhêner:MediaWiki message delivery|MediaWiki message delivery]] ([[Gotûbêja bikarhêner:MediaWiki message delivery|gotûbêj]]) 00:58, 26 nîsan 2026 (UTC)
</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== Guharto ==
: <small>→ Ji rûpela ''[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:berf%C3%AEtik&oldid=6233338 Gotûbêj:berfîtik]'' hatiye girtin.</small>
Silav {{r|Ghybu}} ma hewce ye em guhartoyan jî kategorîze bikin? Li vê [[ژیشک]] binere, tije guhartoyên wê hene, ku em ji gişan re guhandar binivîsin dê 10 navên heman hebin di nav wê kategorîyê de. Tu çi difikirî? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 09:40, 26 nîsan 2026 (UTC)
:{{silav|Keyomerd}}. Erê, problem çî ye? {{=)}} Ew jî tên bikaranîn... Dema ku peyvek tê çêkirin hewce ye ku em hemû agahiyên wan binivîsin: bilêvkirin, bikaranîn, mînak, terîm, herêm, tewandin, etîmolojî...
:Bi min peyvên ku wekî ''guherto'' hatine nivîsandin bi nivîsa {{w|Italic type|îtalîk|ziman=en}} (xwahr) di nav kategoriyan de werin nişan kirin (ji bo cudakirinê). {{ping|Ferhengvan|Bikarhêner}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:40, 30 nîsan 2026 (UTC)
::Yên '''''nestandard''''' di kategorîyan de biferq werin nîşandan baştir e. Qene em karibin wan ji yên stadard ferq bikin. @[[Bikarhêner:Ghybu|Ghybu]] Xêra xwe hûn sîstemeke wisa çêkin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 14:36, 1 gulan 2026 (UTC)
:::Merheba @[[Bikarhêner:Keyomerd|Keyomerd]] û @[[Bikarhêner:Bikarhêner|Bikarhêner]] ev xisûsiyet jixwe di şablonên rêzimanê de heye, wextê {{param|formaalt|1}} dinivîsin, made dikeve Kategoriya [[:Kategorî:Formên alternatîv bi kurmancî]] li gorî koda zimanî jî zimanên din tê çêkirin. Hemû madeyên kurmancî bi alfabeya erebî dikevin vê kategoriyê lê nizanim ev tiştekî rast e Yan xelet e, ji bo nîşandanê min çêkiribû lê min hîn wext nedît ku nîqaşek li ser çêkim. Qaîdeyên bikaranîna vê parametreyê di wîkiya îngilîzî de jî tam ne belî ye. Du gotûbêj [https://en.wiktionary.org/wiki/Wiktionary:Requests_for_moves,_mergers_and_splits#Category:Alternative_forms_by_language] [https://en.wiktionary.org/wiki/Wiktionary:Beer_parlour/2024/June#Decluttering_the_altform_mess] çêkirine lê ji bo îngilîzî ev parametre nehatiye bikaranîn. Min ji bo nîşandanê niha [https://ku.wiktionary.org/w/index.php?title=bax%C3%A7%C3%AA_zarokan&diff=prev&oldid=6241372] ceriband lê bi min xelet e. Em çawa bikin? Kengî vê parametreyê bi kar bînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 8 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Tu çi difikirî? Ji bo zazakî [https://ku.wiktionary.org/w/index.php?title=a_c%C4%B1_geyrayene&oldid=6241396 wisa] bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:55, 8 tîrmeh 2026 (UTC)
:::::Kategorîyekê cuda baş dibe. Wextê ku ez li kategorîya "lekêren bi zazakî" dinêrim, ez hem formê bi şikilê Johnsonê û formê standardê dibînim. Zaten wexte ku ez dikevim ê Johnsonê, min dibe formê standardê. Kategorîyekê cuda, bi taybetî ji bo nivîsandinên alternatîf, baş dibe.
:::::Lê em bîr nekin ku wextê ku em bo formên alternatîf yên devokî qal dikin, belkî adil neye ku em ev bişînin derekê din yê ku însan ewqas nabînin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:03, 8 tîrmeh 2026 (UTC)
::::::@[[Bikarhêner:Kurdanparez|Kurdanparez]] Gelo min rast fêm kir? Tu dibêjî formên {{q|ku|kîvroşk}} bi dîqet werin guhartokirin lê bi yên bi alfabeya Jacobsonê hemû bikin {{param|formaalt|1}}? Formên Jacobsonê dê bikevin Kategoriya [[:Kategorî:Lêker bi zazakî|Lêker bi zazakî]] û [[:Kategorî:Lema bi zazakî|Lema bi zazakî]] yan na? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:55, 8 tîrmeh 2026 (UTC)
:::::::Bi min naxwaze di wan kategoriyan de bikeve. Wek tu dibînî dûplîkat pir hene. Bi min çareya baştir yên Johnsonê di katehgorîyekê din bikevin (bila format li rûpelan neyin guherandin, bi tenê kategorî) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 00:37, 9 tîrmeh 2026 (UTC)
== Peyvên soranî bi latînî ([[:Kategorî:Soranî]]) ==
Silav, di nav vê kategoriyê (''[[:Kategorî:Soranî]]'') de pirr peyv tenê bi alfabeya latînî hatine nivîsandin. Divê ew mutleq jî bi alfabeya erebî werin transkrîbe kirin ji ber ku soranî bi alfabeya erebî tê nivîsand... Bikarênerên ku soranî dizanin dikarin wan sererast bikin? Mînakên guhertinan: [//ku.wiktionary.org/w/index.php?title=zor%C3%AEne&curid=6584&diff=6233355&oldid=5859303], [//ku.wiktionary.org/w/index.php?title=s%C3%AAw&diff=6233097&oldid=6233003], [//ku.wiktionary.org/w/index.php?title=sewz&diff=6233313&oldid=6069107], ... Spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:16, 2 gulan 2026 (UTC)
== <span lang="en" dir="ltr">Vote now in the 2026 U4C election</span> ==
<div lang="en" dir="ltr">
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
</div>
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:15, 27 gulan 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== RFC about AI-generated content in Wikimedia Commons ==
<bdi lang="en" dir="ltr">Apologies for writing in English, please help translate this message to your language. You are invited to participate in a [[c:Commons:Requests for comment/Policy update for AI content|request for comment on Wikimedia Commons about a policy update for AI content]]. This may affect files that are uploaded to Wikimedia Commons for use on this project. Thank you. [[m:User:Codename Noreste|Codename Noreste]] ([[m:User talk:Codename Noreste|gotûbêj]])</bdi> 17:12, 23 hezîran 2026 (UTC)
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== <span lang="en" dir="ltr">Deployment of Legal and Safety Contacts Link in the Footer of Your Wiki</span> ==
<div lang="en" dir="ltr">
<section begin="Message"/>
'''Legal & Safety Contacts'''
Hello community, the Wikimedia Foundation has provided a [[wmf:Special:MyLanguage/Legal:Wikimedia Foundation Legal and Safety Contact Information|single legal and safety contact page]], to be linked in the footer of your wiki, to ensure access to accurate legal information. This is a regulatory requirement. We have already rolled out links to English, German, Italian, Spanish and other wikis and we will deploy to your wiki soon. [[m:Special:MyLanguage/Wikimedia_Foundation_Legal_and_Safety_Contacts_FAQ|Please read more on the project page]] and leave any comments in this thread or on the [[m:Special:MyLanguage/Talk:Wikimedia Foundation Legal and Safety Contacts FAQ|talk page]].
<section end="Message"/>
</div>
-- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:31, 25 hezîran 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=30731267 -->
== Jêbirina ferhenga Sadallah ==
Merheba @[[Bikarhêner:Guherto|Guherto]] Îmkan heye lîsteyeke wisa çêkî ku hemû sayfeyên Cyrusbot çêkir yên ku tê de tenê tercimeyeke îngilîzî heye û ev tercime jî hatibe jêbirin? Nimûne ''[[derdzanî]], [[êşzanî]]'' tenê tercimeya [[pestology]] heye ew jî ji ber heqê telîfê hatiye jêbirin. Nimûneyeke din ''zêrrgeh'', ''zêrrkar'' [[goldsmith]] hatiye jêbirin zêr wekî zêrr hatiye guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:48, 26 hezîran 2026 (UTC)
:Temam @[[Bikarhêner:Wikihez|Wikihez]], lê vê heyvê ez nikarim, heyva were ez ê li vê meseleyê vegerim înşellah. [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 22:51, 2 tebax 2026 (UTC)
== Beşa Tewandin bo kurmancî ==
Bi ya min şablona {{ş|ku-tewîn-nav}} û ya fiîlê em bixin beşa Tewandinê (wekî [[şodir|zazakî]]. Bi vî awayî hem ji aliyê şiklî ve hem jî ji aliyê bikaranînê ve xweştir xuya bike. Eger di qismê Tewandinê de be, ê ku dixwaze îzah û notên xwe yan jî emilandinên din jî dikare binivîse. Hûn çi dibêjin? @[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 6 tîrmeh 2026 (UTC)
:Belkî em dikarin Formata şiweyan wekî ya behdînî jî têxin nav şablonê. Pir caran şiklê tabloya Tercimeyan ji ber dirêjbûna şablona teqînê jî xirab dibe wekî [[astrofizîk]]. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 6 tîrmeh 2026 (UTC)
:Min mijarê te li jorê yê xwe dît, fikirên min jî hene eger baş e. Yê ku te gotiye bi min baş dibe, ji ber ku her rûpel ji vê mîna hev dibe. Em ê rindtir jî ferq li nav devokan binivîsin. [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:44, 7 tîrmeh 2026 (UTC)
::@[[Bikarhêner:Bikarhêner|Bikarhêner]] fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 08:44, 15 tîrmeh 2026 (UTC)
:::Formata ku te teklîf kirîye jî ne xerab e, baş tê xuyan. Feqet bi min halê berê (mewcûd) baştir e. Ji ber ku ev şablon (ku-tewîn-nav) bi piranî kurt in û ji bo her kesî ne mihîm e, lewma li kinarê bi formeke hebe ku karibe were vekirin û girtin baştir e. Lê belê ger şablon dirêjtir bin, em karin wekî ya wîkîya almanî bikin (ew jî wekî teklîfa te ye). -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:50, 15 tîrmeh 2026 (UTC)
::::Em tabloya lêkerê jî rakin? 1)Em wekî navdêran bikin, bin beşa Tewandinê 2) Tabloya {{ş|ku-tewîn-reh}} were şûnda tabloya {{ş|ku-tewîn-lk}}: '''[//ku.wiktionary.org/w/index.php?title=gotin&diff=prev&oldid=6323518]''' , '''[//ku.wiktionary.org/w/index.php?title=vexwarin&diff=prev&oldid=6323525]''' 3) Bi şablona {{ş|tewandin}} û bi parametreyên {{param2|niha}} û {{param2|borî}}: '''[//ku.wiktionary.org/w/index.php?title=vexwarin&diff=6323528&oldid=6323525]'''--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 10:46, 3 tebax 2026 (UTC)
:::::Ez dibêjim em {{ş|ku-tewîn-lk}} bixin bin beşa Tewandinê feqat dema niha û borî tevî formên wan ên negatîv tê de cih bigire, hinek fireh be jî baş e. Dilê min dixwaze yek ji opsiyonên 2 û 3 tevî beşa Tewandinê jî bi kar bînin lê nizanim kîjan hilbijêrin? Herwiha ji bo îsman em dikarin resim zêde bikin ji bo nîşandanê, em ê fiîlan çawa bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:10, 3 tebax 2026 (UTC)
::::::Bi min ji bo kesên ku kurmancî dizanin, tabloya tewandinê "piranî" ne hewce ye. Dema ku ez lêkereke nû hu dikim pêşta rehê wê digerim paşê jî devoka wê... Şablonana "ku-tewîn-reh" pirr dirêj nîne: li ser mobîlê jî pirsgirêk nîne: bin beşa "Lêker"ê de tê nivîsandin: lojîk e. Problema beşa Tewandinê va ye: em ê hertim du rûpel serrast bikinê, rupela lêker (beşa "Tewandinê") û rûpela Tewandin:''lêker''. Bila hêmû gotûbêj tenê li ser rûpela Tewandinê were kirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:06, 3 tebax 2026 (UTC)
:::::::Erê em ku-tewîn-lk bi ku-tewîn-reh biguherînin dê baştir bibe. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:30, 4 tebax 2026 (UTC)
== Reşwî ==
Silav ji we gî re, Pirsekê min heye.
Ez difikirim şablonekê din çê bikim boy devokek kurmancî, yê êla Reşwî. Ew lêkerên xwe hinek cuda ditewînin. Mesela te hem "ez dikim" bêjî yan jî "ez dikime", "em dikinî, em tênî, te em kuştinî" (ne "em dikin") dema xwe yek in lê maneyên xwe ne yek in, "ez bikim" "ez ê bikim" "ez bikime" û dewamên xwe hemû demên xwe yek in lê mane dîsa ne yek in.
Eger min çê kir, şûna xwe li ku derê tînim? Ez difikirim li tewandina devokên kurdî yê ku "tabên" xwe hene "kurmancîya rojavayî", "kurmancîya standard" hsw. Lê tewandin ewqes cuda ye dixwaze ku wî şablona tewandina kurdî pir were guherîn.
Hûn çi dibêjin? (R: Hûn dewên çi?) [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 11:38, 7 tîrmeh 2026 (UTC)
:{{silav|Kurdanparez}} Binêre mînaka devoka ''rojhilatî'': [[Modul:ku-tewandin#L-135]] / [[Modul:ku-tewandin#L-138|L138]] / [[Modul:ku-tewandin#L-143|L-143]], .. Di nav modula ''ku-tewandin"-ê de tê çêkirin. [[Gotûbêja modulê:ku-tewandin#Paşgirên lêkeran di kurmanciya rojhilatî de]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:09, 7 tîrmeh 2026 (UTC)
::PS: Dema niha: [[Modul:ku-tewandin#L-128]]; dema borî: [[Modul:ku-tewandin#L-153]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 13:15, 7 tîrmeh 2026 (UTC)
:::Merheba @[[Bikarhêner:Ghybu|Ghybu]], Eger mimkin be tu dikarî yên devokî têxî binmodulekê û bila modul load bike, bi vî awayî belkî çêkirina yên nû û famkirina modulê hêsantir be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:09, 7 tîrmeh 2026 (UTC)
:::Lê di devokê reşwî de hem dema niha yê statîk heye (Îng: I do -> Ez dikim) û yê progressîf heye (I am doing -> Eîz (nêr) Ez î (mê) dikime, yan jî Ez î dikime/Ez ê dikime). Reşwî ji yê standardê pirtir bo zayend ferq tîne, hem jî li dema pêşî (Ez ê bikim (Nêr) ez a bikim (Mê)). Dema pêşî jî formên cuda hene (Ezêk bikime (I will be doing it) û ez ê/a bikim (I will do it))
:::Ev di modûlê têhel bê? [[Bikarhêner:Kurdanparez|Kurdanparez]] ([[Gotûbêja bikarhêner:Kurdanparez|gotûbêj]]) 22:09, 8 tîrmeh 2026 (UTC)
== WF:Xebat ==
Min sayfeya [[WF:Xebat]] çêkir, xeletiyên ku hûn ferq dikin ji kerema xwe lê zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:35, 15 tîrmeh 2026 (UTC)
== Modul:quote ==
@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ferhengvan|Ferhengvan]] Merheba, ez modula îngilîzî ya jêgirtin û çavkaniyan li gorî şablonên {{ş|jêder}} format dikim û ji bo ku hertişt wekî formata me ya niha be, gelek serê min diêşe. Ez dibêjim bila em dev ji formata niha berdin wekî ya îngilîzî bi kar bînin ji ber ku gelek xisûsiyetên îngilîzî hene ku yekî dikare biemilîne. Hûn ê bibêjin niha ji kû hat bîra tê, min got [[Wîkîferheng:Xebat#MediaWiki:Gadget-start.js|start.js]] nû bikim û min dît otomatîk çavkanî bi riya [[mw:Citoid]]ê bi riya DOI, ISBN û lînk tê çêkirin, min got wiya jî li skrîptê îlawe bikim loma min [[Wîkîferheng:Xebat#Mediawiki:Gadget-citoid-helper.js|Mediawiki:Gadget-citoid-helper.js]] hazir kir lê hin parametreyên Citoidê kêm bûn, min got ka wana rast bikim, min dît formata me ya niha ti standard bi kar nayîne tenê parametreyan bi virgulê li hev dixe.
Ez dibêjim formata îngilîzî baştir e, hem ne îtalîk e ku bi hêsanî tên xwendin, hem jî tarîx û çavkanî herî pêşî tên nîşandan ku ji bo peyitandinê bo min muhîm e. Modula îngilîzî de hema bêje her tişt hatiye çêkirin, eger em rûnin tenê yên lazim li şablona çavkaniyên xwe binivîsin ew ê gelek wext bistîne. Eger em modula îngilîzî bi kar bînin, em ê hem karibin şablonên îngilîzî bi hêsanî û rast kopî bikin hem jî kurdîkirina parametreyan gelek hêsan e. Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:20, 24 tîrmeh 2026 (UTC)
: Ji bo miqayesekirinê bnr. [[:en:Template:quote-book]] / [[Şablon:jêder-kitêb/ceribandin]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:32, 24 tîrmeh 2026 (UTC)
::Bi min bila yê mewcûd jî bimîne, ew jî şiklen xweş û baştir e. Feqet bi min jî yên îngilîzî rehettir in, eger rastkirina yê mewcûd zehmet be, em rasterast wan ji xwe re bînin û tercime bikin. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 09:41, 24 tîrmeh 2026 (UTC)
:::Ez êvarê ve dixebitim, min nêzîk kiribû lê min got belkî hûn qane bibin lê xuya ye hûn ê hîn serê min gelek biêşînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:59, 24 tîrmeh 2026 (UTC)
::::@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Eger îtîrazek tinebe, ez ê îtalîk ji jêgirtinê rakim... Jixwe bi riya [jêgirtin] dikare were veşartin û her daim bi * dest pê dike loma bi hêsanî tê fêmkirin û xwendin rihettir dibe. Bnr. [https://ku.wiktionary.org/w/index.php?title=W%C3%AEk%C3%AEferheng:ceribandin/nim%C3%BBne&oldid=6277132#L%C3%AAker_2 ceribandinê] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:13, 24 tîrmeh 2026 (UTC)
== Request for comment (the future of Abstract Wikipedia) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">
Apologies if this has not yet been translated into your wiki's language. {{int:Please-translate}}
You are invited to voice your opinions in a [[:m:Requests for comment/The future of Abstract Wikipedia|request for comment about the future of Abstract Wikipedia]]. {{Int:Feedback-thanks-title}}
[[:m:User:Kowal2701|Kowal2701]] ([[:m:User talk:Kowal2701|talk]]) 12:25, 24 tîrmeh 2026 (UTC)
</bdi>
<!-- Message sent by User:DreamRimmer@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== ''mê'' yan ''nêr'' li gorî maneyê / ''mê'' yan ''nêr'' (bikaranîn dudil e) ==
[//ku.wiktionary.org/w/index.php?title=xelat&diff=prev&oldid=6291766]: Mixabin pirr bikarhêner şûnda parametreya <code>z=m/n</code> ('' m yan n li gorî maneyê'') <code>z=mn</code> (''m yan n (bikaranîn dudil e)'') bi kar tînin ([[Şablon:navdêr#Parametre]]) ji ber ku di nav her du nivîsê de pir ferq nebînin. Nizanim em ê dîsa parametreya <code>z=nt</code> (''nêtar'') çêkin, wekî berê: [//ku.wiktionary.org/w/index.php?title=W%C3%AEk%C3%AEferheng:D%C3%AEwan/ar%C5%9F%C3%AEv_2019&oldid=6104681#Zayend]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:17, 28 tîrmeh 2026 (UTC)
:Merheba {{r|Ghybu}} {{demo|sep=wisa xuya dibe:<br>|<nowiki>{{navdêr|ku|kelîme|c=mn}}</nowiki>}}
Ya din jî; {{demo|sep=wisa xuya dibe:<br>|<nowiki>{{navdêr|ku|kelîme|c=m/n}}</nowiki>}}
Tu dixwazî em wana berevajî bikin? Not: nêtar ji bo kurmancî nabe, mn û m/n baş in. Berê ji ber ku m/n tinebû, her kes li gorî parametreyên heyî dixebitî. Niha hene bi min problem nîne eger em berevajî nekin.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 05:32, 28 tîrmeh 2026 (UTC)
: Bi min ev format baştir e:
{{demo|sep=wisa xuya dibe:<br>|<nowiki>{{navdêr|ku|kelîme|c1=m|c1etîk=Mêrdîn|c2=n|c2etîk=Dihok}}</nowiki>}}
Yan jî:
{{demo|sep=wisa xuya dibe:<br>|<nowiki>{{navdêr|ku|zayend|c1=n|c2=m|c1ref={{Jêgirtin/Celadet Alî Bedirxan/HAWAR Cild 1 Hejmar 1-23 (1932-1933)/1998|hejmar=4|sal=1938}}|c2ref={{Zana Farqînî}}}}</nowiki>}}
{{çavkanî}}
:--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 05:49, 28 tîrmeh 2026 (UTC)
::Feqat em dikarin li şûna {{param2|c|m/n}}, {{param2|c|mn?}} yan {{param2|c|mn!}} çêkin ji bo ku neyên tevlihevkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 06:00, 28 tîrmeh 2026 (UTC)
:::Ez jî dizanim ku "nêtar ji bo kurmancî nabe" ([//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj%3Afikirmend&diff=3063789&oldid=3063526]) jixwe min "nêtar" kiribû "[//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj:fikirmend&diff=prev&oldid=3063788 mê an nêr li gorî bikaranînê]". Feqet nizanim çima rind nayê fêm kirin: Ferhengvan jî ew parametreyê bi awayekî rast bi kar nayîne [//ku.wiktionary.org/w/index.php?title=Got%C3%BBb%C3%AAj%3Areh&diff=6235991&oldid=6235985]. {{ping|Bikarhêner}} ? Min heta niha pirr gotar serrerastkir... Em ê hertim kontrol bikinê: pêşta problemeke vitira tine bû :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:13, 28 tîrmeh 2026 (UTC)
::::Bi rastî kî li vira parametreyên ku te li jor nivîsandine bi kar tîne {{=D}} ? Ji bîr meke em li ser wîkîferhenga kurdî ne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:39, 28 tîrmeh 2026 (UTC)
:::::Ya muhîm hebûna sîstemekê ye, heta niha ti sîstemeke baş nehatiye çêkirin û ti rê nehatiye dayîn ji bikarhêneran re ku hîn bibin, hêdî hêdî em ê çi ji destê me were, çêkin û hîn bikin. Bi min atolyeyek lazim e em hin tiştan ji bikarhênerên din re hîn bikin. Mîsal min <code><nowiki>{{subst:beralîbike}}</nowiki></code> li wîkîpediyayê çêkir û ji bikarhênerekî re nîşan da çawa tê bikaranîn, wî dest bi bikaranînê kir. Ji ber ku bikarhênerekî mobîlê ye, nikaribû bi tenê serê xwe beralîkin çêke her daîm alîkarî ji kesên din dixwast ku pir carî ti kes nedihat. Însanên me ji aliyê teknîkî ve baş nebin jî, wextê bi riyeke baş îzah dikî, fam dikim şikir ji xwedê re.
:::::Welhasil, [https://ku.wiktionary.org/w/index.php?title=reh&diff=prev&oldid=6291839 reh] çêbû? Eger tu nabêjî nêtar be, min daxwaza te fam nekir. Tu çi dixwazî em çawa bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 28 tîrmeh 2026 (UTC)
::::::Nivîs dîsa "''mê yan nêr li gorî maneyê''" be (kategorî jî) feqet di nav parametreyê de <code>mn</code> neyê nivîsandin. Parametre wekî {{param2|z/c|nt}} bisekiniyana (an jî navekî din "g" an "c") feqet nivîs û kategorî bihata guhertin... Herwiha profîlên bikarhênerên ku tên vira wekî en.wiki nînin: li vir zêde namînin û tiştên ku hu dikin jî ji bîr dikin. PS: Di zimanê peştûyî de jî (nêzîkî zimanê kurmancî ye) peyvên "mê an nêr" (''common gender'') hene [[:en:Category:Pashto nouns with multiple genders]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:28, 28 tîrmeh 2026 (UTC)
:::::::"g", "c", "nt" gî behsa cureyekî xisûsî dikin loma nabin, feqat di nav [https://ku.wiktionary.org/w/index.php?title=Modul:r%C3%AAziman&oldid=6115355#L-306 Modul:rêziman] de koda me "mn" otomatîk dike "mnmane" min ji bo back-compatiblity wisa kiribû lê ya rast "mnmane" ya, em "mn" ji bo kurmancî rakin, hemû "mn" bikin "mnmane" û "m/n" jî rakin û bikin "mndudil", nabe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:44, 28 tîrmeh 2026 (UTC)
::::::::OK, bi min baş e:) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 28 tîrmeh 2026 (UTC)
:::::::::Îmkan heye şablona tewandinê jî wisa bikî ku hem di qismê tewandinê de hem jî aliyê rastê were bikaranîn? Wekî me [[Wîkîferheng:Dîwan/2026#Beşa_Tewandin_bo_kurmancî|li vir]] got? Wextê gelek dirêj be yan mndudil be, em dikarin bixin qismê tewandinê bi şiklê "ya mêr" û "ya nêr". Wekî din yên biçûk û standard bila li aliyê rastê bimînin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:54, 28 tîrmeh 2026 (UTC)
::::::::::Erê bi parametreya {{param2|hêl|çep}} tê çêkirin. Belkî bi JSê tiştekî vitra jî tê çêkirin: dema ku tablo pirr fireh ye (>450px) tablo wekî "veşartî" xuya bike...
::::::::::"''Wekî din yên biçûk û standard bila li aliyê rastê bimînin?''": '''Aliyê rast:''' Dema ku tu mînakan dixwînî bi hêsanî tabloya tewandinê jî seh dikî (pedagojîk e). '''Bin beşa Tewandinê:''' 1) Ji bo mobilan rind e, tablo ne ser maneya peyvê ye. 3) Şûnda maneyan direj dibe 3) Wêne baştir xuya dikin ji ber ku ne bin tabloyê ne. '''Encam''': yek pratîk e, yek jî pedagojîk e [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:27, 28 tîrmeh 2026 (UTC)
:::::::::::Di bin beşa Tewandinê de baştir xuya dike... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:28, 28 tîrmeh 2026 (UTC)
::::::::::::Erê {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:29, 28 tîrmeh 2026 (UTC)
:::::::::::::Feqat lazim e yên mezin wekî [[mustensîx]] di nav qutîyê de bê wekî [[:en:heval]] di mobîlê de ya me sayfeyê fireh dike, ya îngilîzî de dikarî qutîyê bişemitînî [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:44, 28 tîrmeh 2026 (UTC)
::::::::::::::Formata herî xweş [https://ku.wiktionary.org/wiki/mustens%C3%AEx#Kurmanc%C3%AE] resm jî lê tê tablo jî di cihê xwe de ye. Wekî din wextê tablo li aliyê rastê ye însan naxwaze resmekî jî lê zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:54, 28 tîrmeh 2026 (UTC)
:::::::::::::::''overflow'' bi tagên "div"-an rind tên çêkirin. Em ê tabloyên ''NavFrame'' bi kar bînin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 28 tîrmeh 2026 (UTC)
::::::::::::::::Eger class inflection-table bi kar bînî, bi [[MediaWiki:Gadget-defaultVisibilityToggles.js#L-43]] ê ku dixwaze dikare gîya veşêre an nîşan bide. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:11, 28 tîrmeh 2026 (UTC)
:::::::::::::::::Min fêm nekir, biryara we çi ye? Hemû bin beşa Tewandinê? an jî herdu? Li gor wê şablon tê guherandin. Bi min ferq nake, her du jî baş in... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:19, 28 tîrmeh 2026 (UTC)
::::::::::::::::::Me [[#Beşa Tewandin bo kurmancî|li jor]] gotibû bila hemû di beşa Tewandinê de bin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 28 tîrmeh 2026 (UTC)
:::::::::::::::::::Erê, min xwand Bikarnêner tiştekî din gotibû... Tu bi botê çêdikî? Bi botê hemû tabloyan bê parmetreya "hêl" bike bin beşa "Tewandin"-ê. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:30, 28 tîrmeh 2026 (UTC)
::::::::::::::::::::Va rabim botekê hazir bikim. Bi qasî min jê fam kir, tam nabêje hemû nekin, nabêje hemû bikin jî. Lê em hemû bikin çunkî du kes got erê. Cihê Tewandinê piştî Bi alfabeyên din û Formên alternatîv be an jî beriya wan piştî maneyê (piştî wajeya klasîk) be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:38, 28 tîrmeh 2026 (UTC)
:::::::::::::::::::::Eger di yekcarê de nayê çêkirin bi parametreya "hêl" ve çêke. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:42, 28 tîrmeh 2026 (UTC)
::::::::::::::::::::::[https://linkcount.toolforge.org/?project=ku.wiktionary.org&page=%C5%9Eablon%3Aku-tew%C3%AEn-nav 27 hezar] car tê bikaranîn, Eger behsa wextê dikî, ne problem e, eger behsa "tenê yên fireh bixin tewandinê" wê carê yên mê û nêr bixim? Te cewaba pirsê cihê tewandinê jî neda? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:48, 28 tîrmeh 2026 (UTC)
:::::::::::::::::::::::Lojîk dibêje ku piştî bi "alfabeyên din", "formên alternatîv" û "Ji wêjeya klasîk" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:55, 28 tîrmeh 2026 (UTC)
::::::::::::::::::::::::Feqet tabloya tewandinê dûr dikeve... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:00, 28 tîrmeh 2026 (UTC)
:::::::::::::::::::::::::Bi min "Bi alfabeyên din" bixin piştî Bilêvkirinê çûnkî @[[Bikarhêner:Ferhengvan|Ferhengvan]] pir caran ji bo nîşandana dengî alfaba erebî bi kar tîne, h3 rengê teal tenê ji bo "Navdêr, Lêker hwd." bi cssê bi kar bînin [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.css&diff=prev&oldid=6241199 wekî wisa lê berevajî], Piştî "Ji wêjeyê/klasîk" em tewandinê deynîn, "Formên alternatîv" piştî Tewandinê be û îro nekin, eger dixwazî di ceribandinê de tabloya nû hazir bike, em hefteyek li hêviya hevalên din bisekinin, eger îtîraz nehat, em çêkin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:15, 28 tîrmeh 2026 (UTC)
::::::::::::::::::::::::::Beşa "Bikaranîn" jî heye... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:28, 28 tîrmeh 2026 (UTC)
:::::::::::::::::::::::::::Piştî Bikaranînê bnr. [[:en:Wiktionary:Entry_layout#Headings_after_the_definitions]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:37, 28 tîrmeh 2026 (UTC)
::::::::::::::::::::::::::::(h3) Bilêvkirin, Bi alfabeyên din, Sinifa rêzimanî,
::::::::::::::::::::::::::::(h4) Ji wêjeyê/klasîk (divê were xistin bin maneyê), Bikaranîn, Tewandin, Formên alternatîv, Hevmane, Nêzîk, Dijmane, Jê, Têkildar, Etîmolojî (eger du sinifa rêzimanî ji eynî kokê hebe h3) Dûnde, Binêre herwiha, Werger [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:40, 28 tîrmeh 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}} {{r|Ferhengvan|Bikarhêner|Keyomerd|Zerwanda|Kurdanparez}} Fikir? --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:42, 28 tîrmeh 2026 (UTC)
:"Bi alfabeyên din" beşeke pirr hewce nîne, niha beşa "mane" li ser rûpelê dûr dikeve. Bi min "Bîlêvkirin" > Mane > Ji wêjeya klasîk (wekî mînak e) > Bikaranîn > Tewandin > Bi alfabeyên din > Formên alternatîv > ... Divê beşên ku herî zêde tên xwastin werin "ronîkirin". [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 03:07, 29 tîrmeh 2026 (UTC)
::''[[bileks|Bîl'eks]],'' "Bi alfabeyên din" gelek muhîm e, ev ji bo kurmancî "formên alternatîv"-a wîkiya îngilîzî ye, başûrî tenê bi alfaba erebî dinivîsin û wextê kelîmeyek digerin çawa zanibin hatin kîjan kelîmeyê? Bi kirîlî ne lazim e lê bi erebî mecbûrî ye. Ji bo nettir bikin, lazim e em beşa "Bi alfabeyên din" baştir bikin. Mane hinek li jêr be ne muhîm e, wîkiya îngilîzî de jî gelek car lazim e herî jêr ji bo dîtina maneyê. "Ronîkirin"/teal xelet tê famkirin, sankî "qismê sereke (h3) lazim e wê be û qismên din werin xistin binê wê" tê famkirin loma em tenê Mane ronî bikin bes e, ka [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6291927 vê cssê] biceribîne û dîsa fikrên xwe bibêje. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 07:20, 29 tîrmeh 2026 (UTC)
:1. '''Li ser cinsîyetê:''' Bi min, em malûmatên ekstra yên wek ''"li Dihokê nêr, li Serhedê mê"'' (Serhed:m|Dihok:n) li qismê ''"Bikaranîn/Emilandin"''ê bidin baştir e. Kelîme di ferhengan de wek form û cinsîyet bi halê standard cih digirin, eger li mintiqayek cuda be ew cudahî li qismê "Bikaranîn/Emilandin"ê wek malûmatek ekstra cih bigire çêtir e. Ferhenga ku em vê gavê ji bo standardkirinê esas digirin ''"Ji bo Rastnivîsînê Ferhenga Kurdî (Kurmancî) - Tirkî ya Nû"'' de jî bi yek cinsîyetê cih dane kelîmeyan. Ji xeynî wê di ferhenga standardkirinê ya Weqfa Mezopotamyayê de jî ew kelîme bi yek cinsîyetê cih digirin. Tiştekî din jî bêjim, eger em şablonê wek "mndudil" bi nav bikin mimkin e ku rê bide şaşîyan. Çimkî gava mirov wê dibîne, mirov dibêje qey li ser cinsîyeta kelîmeyê dudilîyek heye, bi qenaeta min dudilîyek bi wî şiklî nîne. Divê her kelîme xwedî cinsîyetekê bin û îstîsnayên mintiqayî -ger were xwestin- ekstra were nîşandan. Lewma bi min halê mewcûd baş e. 2. '''Li ser şablonên tewandinê:''' Ger şablonên tewandinê di xuyabûna mobîl de bibin sedema xirabîyê, em wan şablonan ji bo mobîlan têxin qismê "Tewandin"ê. 3. '''Li ser rêzkirina qisman:''' Bi min divê mane li pêş be û ne hewce ye ku qismê "Bi alfabeyên din" derxin pêş. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 13:20, 29 tîrmeh 2026 (UTC)
::Li ser cinsiyetê erê, eger di ferhenga rastnivîsînê de cinsiyet hatibe nivîsandin ne hewce ye em "mndudil" binivîsin li qismê Bikaranînê behskirin bes e, lê eger tê de ev kelîme tinebe û di ferhengên din de jî dudiliyek li ser bikaranîna cinsiyetê hebe, wê demê "mndudil" lazim e bikaranîn çunkî wîkîferheng lazim e li gorî çavkaniyan were nivîsin. Li ser tewandinê ne mimkûnê em di mobîlê de di qismê Tewandinê de, di sermaseyê de em li qismê maneyê bixin loma lazim e em bi yek şiklî li qismê Tewandinê hemû bixin çêtir e. Li ser "Bi alfabeyên din" ji bo min zêde muhîm nîne, feqat dîsa jî ez dibêjim lazim e were pêşxistin. Tiştekî wisa jî heye eger di sayfeyekê de ji yekî zêdetir sinifên rêzimanî hebe wê demê lazim e Bi alfabeyên din bi sê = were bikaranîn, û cihê wê ne li dawiyê lê divê piştî Bilêvkirinê be.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:45, 29 tîrmeh 2026 (UTC)
:Bi a min beşa çevandinê li serê rûpelê be beşa maneyê pir dûrî çavan dikeve. Li jêrtir be çêtir e. [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 06:01, 31 tîrmeh 2026 (UTC)
::{{r|Wikihez}} Bi min di nav tabloyê de parametreya yekem jî were rakirin, ne hewce ye bi <nowiki>{{PAGNAME}}</nowiki> tê çêkirin. Di rûpelên din de jî (ne yên sereke) bi parametreya {{param2|nav}} were nivîsandin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 11:07, 31 tîrmeh 2026 (UTC)
:::Erê, maqûl e. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:23, 31 tîrmeh 2026 (UTC)
::::cinsiyet jî m, n, mn be bes e ma bila wekî {{ş|navdêr}} be. cuda nebin [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:41, 31 tîrmeh 2026 (UTC)
:::::@[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Keyomerd|Keyomerd]], @[[Bikarhêner:Ghybu|Ghybu]] "Bikaranîn" an "Notên bikaranînê" ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:47, 31 tîrmeh 2026 (UTC)
:::::Wekî {{r|Bikarhêner}}î gotî zayenda peyvnan gorî devokan jî diguhere, divê ew jî bêne dîyarkirin. Herwiha beşa lêvandinê a ji bo pîtên cêwî pir tê paşguhkirin. Divê jê re cîyekî cihê bê hilanîn. Û ew jî wek zayendê bi devokan diguhere. Meselen tya pêşîn a "tişt" ê li ba me nerm e lê li deverên din hişk e. Û ji devla reqê (wek t-ya req) hişk bi kar bê çêtir e, jixwe her kes wê bi kar tîne nizanim req ji ku derketîye. [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 14:05, 31 tîrmeh 2026 (UTC)
:
<syntaxhighlight lang="json">
"subsections": [ "Ji wêjeya klasîk", "Ji wêjeyê", "Bikaranîn", "Tewandin", "Bi alfabeyên din", "Formên alternatîv", "Kurtkirin", "Sembol", "Navê zanistî", "Hevmane", "Dijmane", "Jê", "Nêzîk", "Jornav", "Jêrnav", "Têkildar", "Etîmolojî", "Dûnde", "Binêre herwiha", "Bi zaravayên din", "Werger", "Qafiye", "Anagram" ]
</syntaxhighlight>
{{r|Keyomerd|Ghybu|Bikarhêner}}Li gorî nîqaşa jor û [[Alîkarî:Formata nivîsînê]] min ev lîste hazir kir lê tê de hin kêmasî hene, "Bide ber" tê de tine ye, li kû derê bixim? Mihtemelen beşên din jî hene ku haya min ji wan nîne. Pirsek din, beşên nestandard wekî [https://ku.wiktionary.org/w/index.php?title=berf&diff=prev&oldid=6292461] çi bikim? Min got li dawiyê bixim berî Wergerê lê sankî piştî Wergerê be çêtir e?--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:24, 31 tîrmeh 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]], @[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Keyomerd|Keyomerd]] Eger îtîraz an fikrekî din neyê, ez ê beşên nestandard bixim beriya "Bi zaravayên din" eger ev beş tinebe, beriya "Werger". Ez ê "Nêzîk" jî bixim piştî Hevmane û li ser [[Alîkarî:Formata nivîsînê]] "rast" bikim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 1 tebax 2026 (UTC)
::Ez ê "Bikaranîn" jî bikim "Notên bikaranînê" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:03, 1 tebax 2026 (UTC)
:::Jixwe bot hema bêje hazir e, eger dûre kesek rabe fikrekî din li pêş bixe, em dikarin biguherînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:04, 1 tebax 2026 (UTC)
::::Ev hinek zêdetir problematîk e feqat ez ê "Ji wêjeyê/a klasîk" jî bikim "Ji edebiyatê"/"Ji edebiyata klasîk". Tabî eger fikrekî din hebe, ne problem e. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:05, 1 tebax 2026 (UTC)
:::::Na, bi min "wêje" bimînî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 1 tebax 2026 (UTC)
::::::Ok {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:23, 1 tebax 2026 (UTC)
:::::::"Bide ber" pêşiya "Binêre herwiha"
:::::::"Jê" pêşiya "Têkildar" piştî jornav, jêrnav [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:41, 1 tebax 2026 (UTC)
:::::Ji bo min "'''edebîyat'''" çêtir e, bila ne wêje be. "wêje" kelîmeyeke şaşçêkirî ye. Mahîyeta qisman (kîjan qism bi kêrî çi tê) ji navên wan nayê famkirin. "Binêre herwiha" û "Herwiha" tevlihev dibû, baş e we rast kirîye. Feqet mahîyeta wê qismê ("Binêre herwiha") hê jî ji navê wî nayê famkirin. Jixwe ger em bixwazin bi wî şeklî binivîsin jî divê em bikin "Herwiha binêre", ew jî zid hatîye nivîsandin. Ji xeynî wê, "Navê zanistî" na, "'''Navê îlmî'''" çêtir e. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 22:51, 2 tebax 2026 (UTC)
::::::Bi min beşa Navê zanistî werin rakirin, wekî en.wikt em li dawiyê yan li pêşiya maneya bi şablona {{ş|q}} binivîsin yan jî wê şablona îngilîzî çêkin ku enwikt bi kar tîne.
::::::Li wikipediyayê min çend car dîtiye Penaber49 jî "Binêre herwiha" dike "Binêre" loma guhartina wê wekî "Herwiha binêre" fikrekî maqûl e.
::::::Herwiha tu ji bo Bêhtir xwendin çi dibêjî [[Special:PermanentLink/6323554#Binêrê_herwiha]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:29, 3 tebax 2026 (UTC)
:::::::Ez dibêjim bila '''''"Xwendina Zêdetir"''''' be. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 05:03, 6 tebax 2026 (UTC)
:::Em çima peyva notê bi kar tînin, ma "nîşeyên bikaranînê" ne çêtir e? [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 06:04, 1 tebax 2026 (UTC)
:Rêzkirina dawî:
<syntaxhighlight lang="json">
"subsections": ["Ji wêjeya klasîk", "Ji wêjeyê", "Tewandin", "Notên bikaranînê", "Bi alfabeyên din", "Formên alternatîv", "Kurtkirin", "Sembol", "Navê zanistî", "Hevmane", "Nêzîk", "Dijmane", "Jornav", "Jêrnav", "Jê", "Têkildar", "Etîmolojî", "Dûnde", "Bide ber", "Binêre herwiha", "Bi zaravayên din", "Werger", "Qafiye", "Anagram"]
</syntaxhighlight>
Bnr. [[Wîkîferheng:Bot/Balyozbot/tercîh.json]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:44, 1 tebax 2026 (UTC)
:Bi botê ez ê tenê binebeşên sinifên rêzimanî yên bi 4 = biguherînim, yên din lazim e bi destî werin guhartin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 01:46, 1 tebax 2026 (UTC)
::@[[Bikarhêner:Ghybu|Ghybu]] Bota rêzkirina binebeşan û cihguhartina Tewandinê hema bêje hazir e. Param {{param|hêl}} bi kar bînim (eger wisa be, em kategoriya "ku-tewîn-nav bê param hêl" çêkin daku hemû xilas bikin) yan jî şablonên heyî bikim ku-tewîn-nav/kevn û piştî kontrolkirinê hemû bikin nû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:26, 1 tebax 2026 (UTC)
:::Bi parametreyê {{param2|hêl}} tu ê paşê wan dîsa rakî... Herwiha min va [https://ku.wiktionary.org/w/index.php?title=abes%C3%AEv&curid=43641&diff=6294632&oldid=6244736] fêm nekir, me çima Tabloya tewandinê rakir :)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:26, 1 tebax 2026 (UTC)
::::Ji bo ku rê bidin tiştên wisa :D Em ê li kû derê deynîn examples?? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:28, 1 tebax 2026 (UTC)
:::::Tablo hazir bin, em dest pê bikin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:28, 1 tebax 2026 (UTC)
::::::Sernav tê rakirin, mê bikim "m"? yan bila bimînî? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:28, 1 tebax 2026 (UTC)
:::::::Tablo hatiye çêkirin bi [https://ku.wiktionary.org/wiki/Modul:ku-tew%C3%AEn-nav/ceribandin vê] kodê her du parametre jî tên bikaranîn mê/m, nêr/n û mê û nêr/mn... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:41, 1 tebax 2026 (UTC)
:::::Ez nizanim çi bibêjim... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:43, 1 tebax 2026 (UTC)
::::::[https://ku.wiktionary.org/w/index.php?title=abes%C3%AEv&diff=prev&oldid=6294657] ok?
::::::Bi min li şûna {{param|hêl}} em {{ş|ku-tewîn-nav/nû}} bi [[Modul:ku-tewîn-nav/ceribandin|modula ceribandinê]] çêkin, paşê navê şablonê biguherînin baştir be, çunkî [[Special:PermanentLink/6294527#L-337|modula nû]] ne backward compatible e. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:56, 1 tebax 2026 (UTC)
:::::::Em ê dîsa paşê nivîsa "/nû" rakin. Bi botê 22000 rûpel di 2 rojan de tê guherandin? Eger wisa ye (bi min pirr nîne), bi navê rast biguhrîne, bila di nav rûpelên ku nehatine guherandin xeletî hebin... Dîsa jî tu dizanî, bot te çêkiriye û tu sererast dikî :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:17, 1 tebax 2026 (UTC)
::::::::Min gelek caran dîtiye, bi botê tiştekî hatiye guhartin lê ji ber ku rind nehatiye kontrolkirin, şablona kevn bi halê xwe yê kevn maye. Tirsa min ew e. Ji ber ku formata hemû sayfeyan ne eynî ne, ez dibêm bot gelek ji wan derbas bike. Bi vî awayî em ê {{ş|ku-tewîn-nav}} sêwî bihêlin û piştî bot xilas kir, em ê bi dil rehetî karibin şablona nû rakin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:26, 1 tebax 2026 (UTC)
:::::::::Rast e! {{param|g|2}} :)) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:32, 1 tebax 2026 (UTC)
::::::::::Mîsalên wan gelek in :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:36, 1 tebax 2026 (UTC)
:::::::::::Eger karibî hinek round edges bixî û rengê background a "Tewandina X" biguherînî, gelek moderntir û xweştir xuya bike :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:25, 1 tebax 2026 (UTC)
::::::::::::''Simplicity is best'' / ''Fewer choices mean less worry:'' felsefeya min e:) Me tabloyê wekî en.wîkî kir [https://ku.wiktionary.org/wiki/%C5%9Eablon:tabloya-tewandin%C3%AA-ser {tabloya-tewandinê-ser}], tenê [//ku.wiktionary.org/wiki/Modul:ku-tew%C3%AEn-nav/ceribandin#L-362 reng] ({{param2|palette}}) tê guherandin (felsefeya wan e jî...). Bi min tiştekî pir rind çêkirine. Dîsa jî em dikarin (wekî berî) bi CSSê biguherînin. Ji bo çêkirina tabloyan min di rûpela şablonê de tiştên ku pirr hewce ne nivisandiye ji bo detayan jî rûpela en.wiki heye. Herwiha tabloyê li gor daxwaza we biguherînin û serrast bikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 03:04, 2 tebax 2026 (UTC)
:::::::::::::PS: Şiklê tabloyê li vira tê guherandin [[Şablon:tewandin-ser/style.css]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 04:41, 2 tebax 2026 (UTC)
::::::::::::::Mala te ava tu ji heqê rengan gelek xweş derdikevî :)
::::::::::::::Ev felsefeya te gelek caran karê min pir zor dike {{=D}} en.witk gelek tiştî bi awayekî xweş çêkirine, ez jî heta ku wana adapte bikim, gelek êş dikşînim bnr. [[#Modul:quote]] û Şablonên etîmolojiyê [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:47, 2 tebax 2026 (UTC)
:::::::::::::::Bot xilas kiriye, Baş e [[Special:PermanentLink/6323268#Şablona_ku-tewîn-nav_ne_li_binê_Serenav_an_Navdêrê_ye|zêde]] xeletî derneketin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:35, 2 tebax 2026 (UTC)
::::::::::::::::Gelek spas! [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:42, 2 tebax 2026 (UTC)
:::::::::::::::::Niha şablon sêwî ma ji xeynî gotûbêjan, ew jî dûrê rast bikim, tu dikarî modulan rojane bikî? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:28, 3 tebax 2026 (UTC)
::::::::::::::::::Spas. Ev rûpel pirr dirêj bû... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:33, 3 tebax 2026 (UTC)
:::::::::::::::::::Erê, rêzkirina beşan ez ê bikim yek satir, lê dîsa jî zêde fêde nede, fikrekî din heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:38, 3 tebax 2026 (UTC)
::::::::::::::::::::@[[Bikarhêner:Ghybu|Ghybu]] [https://ku.wiktionary.org/wiki/W%C3%AEk%C3%AEferheng:D%C3%AEwan#c-Wikihez-20260801005000-Wikihez-20260731162400] û [https://ku.wiktionary.org/w/index.php?title=gur&curid=5384&diff=6328092&oldid=6328089] ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:00, 7 tebax 2026 (UTC)
== SEO ==
Merheba @[[Bikarhêner:Ghybu|Ghybu]] Wextê ''jiyannas'' bi şiklê "jiyannas" lê digerî di nav netîceyên googlê de [[jiyannas]] derdikeve? Min hem bi Chromeê û hem jî bi VPNê ceriband lê [[jiyannaseke]] derdikeve feqat [[jiyannas]] dernakeve. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:31, 2 tebax 2026 (UTC)
:"zengilok" derdikeve [[zengilok]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:38, 2 tebax 2026 (UTC)
:Min jî tenê [https://usercontent.one/wp/portal.arsivakurd.org/wp-content/uploads/2024/08/Bernamegeh-4.pdf?media=1709107483 va] (''Berna Megeh'', Jan Dost, OCCO MAHABAD) û https://portal.arsivakurd.org/wp-content/uploads/2022/06/Xwebun-34.pdf va] (''Xwebûn'', hj.34) dît herwiha ''Jihannasî'' jî heye... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:44, 2 tebax 2026 (UTC)
::Behsa çavkaniyan nakim behsa SEOyê dikim, çima [[jiyannas]] di netîceyan de dernakeve. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:49, 2 tebax 2026 (UTC)
:::Min fêm nekir, pirsa te çima Wîkîferheng dernakeve ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:51, 2 tebax 2026 (UTC)
::::Erê çima sayfeya Wîkîferhengê ya [[jiyannas]] dernakeve feqat [[jiyannaseke]] derdikeve. Min du nimûne nivîsandin ka piştî mehekê em binêrin derkeve yan na. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:54, 2 tebax 2026 (UTC)
:::::Bi min dema ku motora googlê "jiyannaseke" (-eke bi kurdî ye) digere, ew peyv tenê di kurdî de heye ji ber vê yekî dide. Feqet "Jiyannas" di nav zimanên din de jî heye (belkî difikire ku ew xeletî ye.). Herwiha nivîsa bin "Jiyannas" li gor "jiyannaseke" biçûk e û li bin "biolog" hatiye nivîsandin:) Herwiya peyva "biolog" jî dide ew jî giridayî pirr peyvan e... Pirr parametre hene... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:09, 2 tebax 2026 (UTC)
::::::Wextê li "birakujî" dictionnaire digerî netîceya ewil rasterast fr.wikt e. Wana tiştekî kirine lê çi? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:13, 2 tebax 2026 (UTC)
:::::::"Trust Score" ? Herwiha peyva "birakujî" pêş Wîkîfehengê hatiye çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:21, 2 tebax 2026 (UTC)
::::::::Nizanim [[:en:Wiktionary:Votes/2023-08/Installing the WikiSEO extension]] heye lê [[:phab:T346590]] sekinî ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:27, 2 tebax 2026 (UTC)
:::::::::Erê, feqet lînkên û navên Wîkîferhenga fransî û îngilîzî di internetê de pirr tên parvê kirin. Wîkîferhenga fansî ferhengek derxistiye: [https://www.fnac.com/a13530183/Collectif-Le-dico-adapte-du-Wiktionnaire Le dico] (li ser Amazonê). Kî qala Wîkîferhenga kurdî dike? Ji ber vê yêke hertim pêş me ne. Daxwaza wan werin pêş malperên din... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:36, 2 tebax 2026 (UTC)
::::::::::[https://www.google.com/search?sca_esv=a854c327b7bb0786&q=%22W%C3%AEk%C3%AEferheng%22&tbm=nws&source=lnms&fbs=ABfTbFUDadgeu2mn4mYJ8iEZ1GUDDuwRa4um-2LNhBFWIpqZc7ZL_t3iAe0lB1D4hb_Ohp7yg8FO-XwZ7R-ba4YefvIQCF4Ra4wbUNQStQ8dVc6IK2vWHoXbgHLwJobq4HTNMksBhYHBmxw2hK9Sa4pHAI3I2Ovzox54EiEId7q7IoMJRkRlr-XDezqDqx33xDsZIWuHy-a_vqoqDlYrhNBbNeVEP9MwaA&sa=X&ved=2ahUKEwiz94Co9IKWAxXNUaQEHQJAAGYQ0pQJegQIGBAB&biw=1320&bih=583&dpr=1 Wîkîferheng] {{=D}} Li ser X û medyayên din de jî pirr qala Wîkîferhengê nakin... Bi rastî kî Wîkîfrhengê dixwîne? [https://pageviews.wmcloud.org/topviews/?project=ku.wiktionary.org&platform=all-access&date=yesterday&excludes=]... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:41, 2 tebax 2026 (UTC)
:::::::::::Haya ti kesî ji Wîkîferhengê nîne bi rastî loma pêşxistina SEOyê hewce ye. Ez dibêjim miheqeq fr.wikt tiştek çêkiriye ne mimkun e wextê heval dictionnaire binivîsî wiktionary derkeve bêyî çêkirina tiştekî. Not: Min ferq kir li ser fr.wikt wextê bi mobîlê tê dikevî, eger di sayfeyekê de du ziman hebe ya fransî otomatîk vedibe zimanê din veşartî dimîne. Fikrekî xweş e em jî çêkin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:21, 2 tebax 2026 (UTC)
::::::::::::Erê bila were çêkirin. Min gotûbêja wan li ser vê mijarê xwandibû, Wîkîferhenga fransî jî ne razî ye. Dema ku li ser Googlê tu peyvek diguherî Wîkîferhenga wan di bin Wîkîpediyayê û di bin malperên din de derdikeve. Û çend caran şûnda "Wiktionnaire" "Wikipédia" tê nivîsansin... Ew jî nizanin çi bikin :)) Niha dizanim ku en.wikt ji ne razî ye... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:37, 2 tebax 2026 (UTC)
:::::::::::::@[[Bikarhêner:Ghybu|Ghybu]], @[[Bikarhêner:Wikihez|Wikihez]]: Ez dibêjim rûpelên ku tenê form di wan de hene, divê <nowiki>__NOINDEX__</nowiki> bin. Wî çaxî, rêza wan nikare ji lemmayê bilindtir be ji ber ku wê nînbin. Ez dibêjim belkî îhtîmala derketina lemmayê bêhtir jî bibe heke netîceyên ku dişibin wê nînbin. Jixwe lemma wê derkeve heke yek li formekê bigere, ji ber ku di rûpelê lemmayê de tabloyên tewandinê hene û ev tên jimartin. [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 22:55, 2 tebax 2026 (UTC)
::::::::::::::@[[Bikarhêner:Guherto|Guherto]] Em dibêjin tenê form xuya dike ya sermade xuya nake tu dibêjî em formê noindex bikin {{=D}}
::::::::::::::Claude [[Special:PermanentLink/6323375|li vir]] dibêje ya ku Ghybu got rast e, ji ber ku frwikt gelek tê bikaranîn bêtir dikeve ber çavê google. Eger rojek Wikiseo li en.wikt were çêkirin, em jî bixwazin qet nebe kêrî tiştekî were.
::::::::::::::Em şablonek ji bo metadescription çêkin? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:09, 2 tebax 2026 (UTC)
:::::::::::::::@[[Bikarhêner:Wikihez|Wikihez]] Min got, ''Wî çaxî, rêza wan nikare ji lemmayê bilindtir be ji ber ku wê nînbin. Ez dibêjim belkî îhtîmala derketina lemmayê bêhtir jî bibe heke netîceyên ku dişibin wê nînbin.'' [[Bikarhêner:Guherto|Guherto]] ([[Gotûbêja bikarhêner:Guherto|gotûbêj]]) 23:10, 2 tebax 2026 (UTC)
:::::::::::Em jegirtinan lînk bikin? Bi vî sayî gelek sayfe lînk bide ser hev. Bi riya cssê em dikarin rengê tarî bikin da ku wekî lînk xuya nekin... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:39, 3 tebax 2026 (UTC)
::::::::::::Wextê jîwar ferheng yan jî boçik ferheng lê digerî rasterast Wîkîferheng dertê. Meselê hejmara bikaranîna eynî kelîmeyê di sayfeyê de ye. Em çiqas jêgirtin lê zêde bikin ewqas baştir dibe netîce :) Google AI Mode rasterast Wîkîferheng wek çavkanî nîşan û lînk dide loma kurmanc bi kar neyînin jî AI bi kar tînin baş e ... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:24, 3 tebax 2026 (UTC)
:::::::::::::Êêêê Google AI Overview çi bi şikle "tarîxiyen" ferheng lê digerim wekî ku min li ferhengê qeyd kiriye gelek xweş îzah dike (ji ber ku hîn gelek nû ye netîceyan de Wîkîferheng dernakeve) Nimûneyên bikaranînê jî ji înternetê derdixe. Çi mana ferhengê nemaye... Em çi îşî dikin li vir :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:47, 3 tebax 2026 (UTC)
:::::::::::::Êdî Google jî nayê bikaranîn :)) Niha kî tere ser ''Stack Overflow'' û di nav belgeyên kodan de digere... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:47, 3 tebax 2026 (UTC)
::::::::::::::Erê welle, ez gelek car dibînim kesên derdora min hemen Gemini an Chatgpt vedikin, çi tiştê biçûk hebin, hemen pirs dikin, qet li ser Googlê jî lê nagerin :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:52, 3 tebax 2026 (UTC)
:::::::::::::::"êgsig ferheng" netîce nade feqat wexta "ornag ferheng" digerim rasterast wîkîferheng derdikeve li binê dibêje "Tewandina ornag. Zayenda nêr a binavkirî. Rewş, Yekjimar, Pirjimar. Navkî, ornag, ornag. Îzafe, ornagê · ornagên. Çemandî, ornagî · ornagan.Read more"
:::::::::::::::Meraq dikim guhartina cihê tewandinê çi netîce bide. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:21, 3 tebax 2026 (UTC)
::::::::::::::::Pêşta me tabloya Tewandinê vitira dinivîsand: [[ketin (Tewandin)]] Dma ku nav hat guherandin li ser Googlê tewandin winda bû...
::::::::::::::::Min tewandin:kirin geriya tabloya tewandina Wîkîferhengê dide. Êdî kes nê vira:) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:38, 3 tebax 2026 (UTC)
:::::::::::::::::Tenê dema "niha" nivîsand. Min jî bi kurdî "dema bê jî binivîse" nivîsî: ew jî nivîsand... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:42, 3 tebax 2026 (UTC)
::::::::::::::::::[https://support.google.com/webmasters/answer/16908024?hl=en tê asteng kirin]... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 02:07, 4 tebax 2026 (UTC)
:::::::::::::::::::kî dibêje? li wir tiştek wisa nabêje...? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 02:16, 4 tebax 2026 (UTC)
::::::::::::::::::::Va hatiye nivîsandin: "''Exclude my site's links and content from Search generative AI features: Your site's content is prevented from being visible to users in Search generative AI features, including being linked to within these features and helping with grounding AI responses in these features. You won't receive any traffic or impressions from these features. Learn more about what excluding your site means.''" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 02:23, 4 tebax 2026 (UTC)
== Moda tarî ==
Bi chrome û Firefoxê wextê Reng dikî "otomatîk", eger tarî be, tiştên bi reng xirab xuya dikin, lê wextê "tarî" hildibijêrî xweş xuya dikin. Min hemû js/css/Gadget betal kirin û ?safemode=1 ceriband, dîsa jî wextê "otomatîk" e, her tiştê tarî xirab e. Êdî nizanim problem li cem min e yan di Moda tarî de ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:15, 9 tebax 2026 (UTC)
== Formata jêgirtinan ==
Merheba hevalno {{r|Ghybu|Bikarhêner|Keyomerd|Kurdanparez|Guherto|Şêr}} Wekî min [[#Modul:quote|li jor]] jî gotiye, formata îngilîzî ji ya me baştir e. Her tişt di cihên xwe de ne. Ya me wexta çend tercime yan jî transliterasyon îlawe dikî, gelek tevlihev dibe. Berê me çavkaniyê bi amûra [[MediaWiki:Gadget-jeder.js]] kurt kiribû feqat ew jî ne tiştekî baş e ji ber ku gelek malûmat tên veşartin û kesên nû tên tenê sal û nivîskarê dinivîsin carinan ew jî ne tiştekî baş e. Em dikarin amûra jêderê wisa eyar bikin ku bêyî parentez bixebite û ewilî vekirî be, dûre ê ku dixwaze malûmatan kurttir bike. Eger em vî tiştî bi formata me ya niha bikin, jêgirtinên bi çavkaniyên dirêj dê gelek xirab xuya bikin.
Hûn dikarin li vir miqayese bikin ka kîjan baştir xuya dike:
# [[Special:PermanentLink/6329434|jêgirtina dirêj bi îngilîzî bi formata en.wikt]] vs. [[Special:PermanentLink/6329414|bi formata me ya niha]]
# [[Special:PermanentLink/6329439|jêgirtineke bi soranî bi formata en.wikt]] vs. [[Special:PermanentLink/6329437|jêgirtineke bi soranî bi formata me ya niha]]
Ez naxwazim xwe wisa nîşan bidim ku li ser eynî tiştî her daîm israr dike feqat bi ya min ev zarûriyeteke ji bo baştirkirina xwendina nimûneyan e. Loma ez li vir dengdanek li dar dixim, bila qerar çi be, em wisa bikin, dûre em dikarin amûreke nû çêkin ku li gorî tercîhên bikarhênerî formata en.wikt yan jî formata niha were bikaranîn.
Ji kerema xwe raya xwe li jêr bidin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:01, 11 tebax 2026 (UTC)
=== Dengdan ===
{{dengdan|destpêk=11.08.2026|dawî=18.08.2026}} <!-- kengî dengdan xilas bû vê şablonê {{dengdan/borî|destpêk=|dawî=}} bi kar bînin. -->
==== Bila formata niha bimîne ====
<!-- Li jêr '# {{erê}} ~~~~' binivîse ji bo raya erê. -->
==== Bila formata en.wikt were bikaranîn ====
<!-- Li jêr '# {{erê}} ~~~~' binivîse ji bo raya erê. -->
# {{erê}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 11 tebax 2026 (UTC)
# {{erê}} [[Bikarhêner:Keyomerd|Keyomerd]] ([[Gotûbêja bikarhêner:Keyomerd|gotûbêj]]) 14:40, 16 tebax 2026 (UTC)
=== Gotûbêj ===
{{şirove}} Eger hûn bibêjin çima sal û çavkanî li herî pêş e sebeb ev e: Ji bo en.wiktê "attestion" yanî peyitandin bi riya emilandinên heqîqî pêk tê. Yanî eger di ferhengekê de ev kelîme derbas bibe ji bo wan ne muhîm e, ya muhîm emilandina di nav cumleyan de ye. Mîsal ew kelîmeyên ku tenê ferhengan de derbas dibin dixin [[:en:Appendix:English dictionary-only terms]]. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:14, 11 tebax 2026 (UTC)
:@[[Bikarhêner:Ferhengvan|Ferhengvan]], @[[Bikarhêner:Bibliophile|Bibliophile]] û @[[Bikarhêner:Dûrzan|Dûrzan]] fikrê we çi ye li ser vê meseleyê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:48, 12 tebax 2026 (UTC)
::Ez ji tiştên teknîk pirr fêhm nakim. Çewa baş be ên ku pispor in bila biryar bidin... [[Bikarhêner:Dûrzan|Dûrzan]] ([[Gotûbêja bikarhêner:Dûrzan|gotûbêj]]) 21:04, 12 tebax 2026 (UTC)
== Guhartina rêbaznameyê ==
Ez fam nakim me çima di rêbaznameyê de gotiye twitter nayê hesibandin wextê çavkanîdayînê. en.wikt bi kar tîne û eger wek cimleyek derbas bibe bi min ti problem nîne. Fikr? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 14 tebax 2026 (UTC)
:Heta twitter ji bikaranîna ferhengan hîn baştir e, qet nebe em emilandina kelîmekeyê ya eseh dibînin... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:07, 14 tebax 2026 (UTC)
70xs1uojrmteix1f7l0padishvgg4zx
MediaWiki:Gadget-start/data.json
8
1357737
6334211
6328251
2026-08-16T20:12:46Z
Wikihez
7344
6334211
json
application/json
{
"langChoices": {
"ku": "kurmancî",
"zza": "zazakî",
"ckb": "soranî",
"sdh": "kurdiya başûrî",
"hac": "hewramî",
"de": "almanî",
"ar": "erebî",
"fa": "farisî",
"fr": "fransî",
"fi": "fînî",
"en": "îngilîzî",
"ja": "japonî",
"sv": "swêdî",
"tr": "tirkî"
},
"posChoices": {
"navdêr": "Navdêr (îsm)",
"lêker": "Lêker (fiîl)",
"rengdêr": "Rengdêr (sifet)",
"hoker": "Hoker (zerf)",
"serenav": "Serenav (îsmê xisûsî)",
"artîkel": "Artîkel",
"baneşan": "Baneşan (nîda)",
"bazinedaçek": "Bazinedaçek",
"cînav": "Cînav (zemîr)",
"daçek": "Daçek (edat)",
"girêdek": "Girêdek",
"gotineke pêşiyan": "Gotineke pêşiyan",
"hejmar": "Hejmar",
"hevok": "Hevok (cimle)",
"îfade": "Îfade",
"kurtenav": "Kurtenav",
"navgir": "Navgir",
"paşdaçek": "Paşdaçek",
"paşgir": "Paşgir",
"pêşdaçek": "Pêşdaçek",
"pêşgir": "Pêşgir",
"pirtik": "Pirtik",
"reh": "Reh",
"sembol": "Sembol",
"tîp": "Tîp (herf)"
},
"posExamples": {
"navdêr": "Kelîmeyên ji bo tiştan (sêv, heywan, qedeh) yan terman (dilşadî, bextiyarî hwd.) di rêzimanê de.",
"lêker": "Kelîmeyên ji bo kirin an bûyînê wekî 'nivîsîn' yan 'xeber dan'.",
"rengdêr": "Kelîmeyên ku wesfa navdêran nîşan didin wekî 'sêva sor' yan 'xwarina şîrîn'.",
"hoker": "Kelîmeyên ku dem, cih yan awayê kirinê diyar dikin wekî 'bi vî awayî' yan 'demkî'.",
"daçek": "Kelîmeyên ku cih, dem yan hereketê diyar dikin wekî 'di.. de' yan 'ji'.",
"cînav": "Kelîmeyên ku li şûna navdêran tên bikaranîn daku navdêr neyên dubarekirin wekî 'ew' yan 'em'.",
"baneşan": "Kelîmeyên ku hest, deng hwd. îfade dikin wekî 'noşî!' yan 'aferîn'.",
"serenav": "Navdêrên ku şexsiyetên ferdî nîşan didin, wek navên kes, cih an organîzasyonan nimûne 'London' yan 'Diyarbekir'.",
"gotineke pêşiyan": "Gotinên ku di nav xelkê de bi berbelavî tên bikaranîn bo nimûne 'Serê neêşe ne hewceyî destmalê ye'."
},
"genderLabels": {
"m": "mê",
"n": "nêr",
"nt": "nêtar",
"mn": "mê yan nêr",
"mnmane": "mê yan nêr li gorî maneyê",
"mndudil": "mê yan nêr bikaranîn dudil e",
"g": "giştî",
"y": "yekjimar",
"p": "pirjimar"
},
"tplDefs": {
"rastnivîs": {
"langs": [
"ku"
],
"noLang": true
},
"ku-Latn": {
"langs": [
"ku"
],
"noLang": true
},
"ku-tewîna-nav": {
"langs": [
"ku"
],
"noLang": true
},
"sdh-Arab": {
"langs": [
"sdh"
],
"noLang": true
},
"hac-Arab": {
"langs": [
"hac"
],
"noLang": true
},
"ja-romanîzekirin": {
"langs": [
"ja"
],
"noLang": true
},
"ckb-Arab": {
"langs": [
"ckb"
],
"noLang": true
},
"zza-awayê-Jacobsonê": {
"langs": [
"zza"
]
},
"awayekî din": {
"langs": null
},
"guharto": {
"langs": null
},
"hevmaneya": {
"langs": null
},
"kevnbûyî": {
"langs": null
},
"kêmpeyda": {
"langs": null
},
"kurtkirin": {
"langs": null
},
"paşnav": {
"langs": null
},
"şaşnivîs": {
"langs": null
}
},
"jederDefs": {
"jêder-kitêb": {
"label": "Kitêb",
"fields": {
"nivîskar": "Nivîskar",
"sernav": "Sernav",
"sernavê orjînal": "Sernavê orjînal",
"terciman": "Terciman",
"weşanxane": "Weşanxane",
"cih": "Cih",
"sal": "Sal",
"çap": "Çap",
"rûpel": "Rûpel",
"isbn": "ISBN",
"url": "URL"
}
},
"jêder-kovar": {
"label": "Kovar",
"fields": {
"nivîskar": "Nivîskar",
"nivîskar2": "Nivîskar 2",
"sernav": "Sernav",
"sernavê orjînal": "Sernavê orjînal",
"kovar": "Kovar",
"terciman": "Terciman",
"weşanxane": "Weşanxane",
"cild": "Cild",
"hejmar": "Hejmar",
"sal": "Sal",
"rûpel": "Rûpel",
"url": "URL"
}
},
"jêder-websîte": {
"label": "Websîte",
"fields": {
"nivîskar": "Nivîskar",
"sernav": "Sernav",
"terciman": "Terciman",
"websîte": "Websîte",
"url": "URL",
"arşîv": "Arşîva URLê",
"sal": "Sal",
"tarîx": "Tarîx"
}
}
},
"pronTpls": {
"ku-IPA": {
"desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)",
"snippet": "* {{ku-IPA}}",
"langs": [
"ku"
],
"noLang": true
},
"ku-kîte": {
"desc": "Hecekirina otomatîk ji bo kurmancî",
"snippet": "* {{ku-kîte}}",
"langs": [
"ku"
],
"noLang": true
},
"zza-IPA": {
"desc": "IPAya otomatîk ji bo zazakî",
"snippet": "* {{zza-IPA}}",
"langs": [
"zza"
],
"noLang": true
},
"zza-kîte": {
"desc": "Hecekirina otomatîk ji bo zazakî",
"snippet": "* {{zza-kîte}}",
"langs": [
"zza"
],
"noLang": true
},
"IPA": {
"desc": "Alfabeya fonetîk a navmiletî (IPA)",
"snippet": "* {{IPA|CODE|/…/}}"
},
"deng": {
"desc": "Nimûneya telafûzê bi dosyeya dengî",
"snippet": "* {{deng|CODE|navê_dosyayê.ogg|dever=}}"
},
"bilêv": {
"desc": "{{bilêv|herf|nerm, req|hejmar}}",
"snippet": "* {{bilêv|herf|cure|hejmar}}"
},
"kîte": {
"desc": "Şablona hecekirinê",
"snippet": "* {{kîte|CODE|he|ce}}"
},
"hevdeng": {
"desc": "Hevdeng — gotinên bi heman dengî",
"snippet": "* {{hevdeng|CODE|…}}"
}
},
"etymTpls": {
"ji": {
"desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}",
"snippet": "{{ji|SOURCELANG|CODE|…}}"
},
"deyn": {
"desc": "Deynkirî — {{deyn|lang|kod|kelîme}}",
"snippet": "{{deyn|SOURCELANG|CODE|…}}"
},
"mîr": {
"desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}",
"snippet": "{{mîr|SOURCELANG|CODE|…}}"
},
"qertaf": {
"desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}",
"snippet": "{{qertaf|CODE|…|…}}"
},
"q": {
"desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve",
"snippet": "{{q|CODE|…}}"
},
"hv": {
"desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{hv|SOURCELANG|…}}"
},
"nh": {
"desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{nh|SOURCELANG|…}}"
},
"cotreh": {
"desc": "Cotreh — teror {{cotreh|ku|tirs}}",
"snippet": "{{cotreh|CODE|…}}"
},
"yekser": {
"desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}",
"snippet": "{{yekser|SOURCELANG|CODE|…}}"
},
"wergirtin": {
"desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}",
"snippet": "{{wergirtin|SOURCELANG|CODE|…}}"
},
"paşvesazî": {
"desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}",
"snippet": "{{paşvesazî|CODE|…}}"
},
"qusandin": {
"desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}",
"snippet": "{{qusandin|CODE|…}}"
},
"têkilkirin": {
"desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}",
"snippet": "{{têkilkirin|CODE|…}}"
},
"nyz": {
"desc": "Nayê zanîn — {{nyz|ku|hûr=1}}",
"snippet": "{{nyz|CODE}}"
},
"onom": {
"desc": "Onomatopoetîk — {{onom|ku|hûr=1}}",
"snippet": "{{onom|CODE}}"
},
"navmiletî": {
"desc": "Navmiletî — {{navmiletî|ku|hûr=1}}",
"snippet": "{{navmiletî|CODE}}"
},
"neo": {
"desc": "neolojîzm — {{neolojîzm|ku|hûr=1}}",
"snippet": "{{neolojîzm|CODE}}"
}
},
"metadata": {
"aa": {
"haswikt": 1,
"sc": [
"Latn",
"Ethi"
]
},
"ab": {
"haswikt": 1,
"sc": [
"Cyrl",
"Latn",
"Geor"
]
},
"aer": {
"sc": "Latn"
},
"af": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ak": {
"haswikt": 1
},
"akk": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Xsux"
},
"als": {
"haswikt": 1
},
"am": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1,
"sc": "Ethi"
},
"an": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ang": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"ar": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"alt": 1,
"haswikt": 1,
"sc": "Arab"
},
"arc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": [
"Hebr",
"Syrc"
]
},
"are": {},
"arz": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Arab"
},
"as": {
"haswikt": 1,
"sc": "Beng"
},
"ast": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"av": {
"haswikt": 1
},
"ay": {
"haswikt": 1
},
"az": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl",
"Arab"
]
},
"ba": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"bar": {},
"bat-smg": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"bbl": {
"sc": "Geor"
},
"be": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"be-x-old": {
"sc": "Cyrl"
},
"bg": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"bh": {
"haswikt": 1,
"sc": "Deva"
},
"bhb": {
"sc": "Deva"
},
"bi": {
"haswikt": 1
},
"blt": {
"sc": "Tavt"
},
"bm": {
"haswikt": 1,
"sc": [
"Latn",
"Nkoo",
"Arab"
]
},
"bn": {
"haswikt": 1,
"sc": "Beng"
},
"bo": {
"haswikt": 1,
"sc": "Tibt"
},
"br": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ca": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"cdo": {
"sc": "Hani"
},
"ce": {
"sc": "Cyrl"
},
"ch": {
"haswikt": 1
},
"chr": {
"haswikt": 1,
"sc": "Cher"
},
"cjy": {
"sc": "Hani"
},
"ckb": {
"haswikt": 1,
"sc": "Arab",
"wsc": "ku-Arab"
},
"cmn": {
"sc": "Hani",
"wiktprefix": "zh"
},
"co": {
"haswikt": 1
},
"cpx": {
"sc": "Hani"
},
"cr": {
"haswikt": 1,
"sc": "Cans"
},
"crh": {},
"cs": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"csb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"cu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": [
"Cyrs",
"Glag"
]
},
"cv": {
"sc": "Cyrl"
},
"cy": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"czh": {
"sc": "Hani"
},
"czo": {
"sc": "Hani"
},
"da": {
"g": [
"g",
"nt",
"g-p",
"nt-p",
"p"
],
"haswikt": 1
},
"dax": {},
"de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"dhg": {},
"djb": {},
"dji": {},
"djr": {},
"dng": {
"sc": "Cyrl"
},
"dsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"dsx": {},
"duj": {},
"dv": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Thaa"
},
"dz": {
"haswikt": 1,
"sc": "Tibt"
},
"el": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"en": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"eo": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"es": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"et": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ett": {
"g": [
"y",
"p"
],
"sc": "Ital"
},
"eu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fa": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-cls": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-ira": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fi": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fil": {},
"fj": {
"haswikt": 1
},
"fo": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1
},
"fr": {
"g": [
"n",
"m",
"mnmane",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"frm": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fro": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fur": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fy": {
"g": [
"g",
"nt",
"p"
],
"haswikt": 1
},
"ga": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"gan": {
"sc": "Hani"
},
"gd": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gez": {
"sc": "Ethi"
},
"gl": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gmw-ecg": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gmy": {
"sc": "Linb"
},
"gn": {
"haswikt": 1
},
"gnn": {
"sc": "Latn"
},
"got": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"sc": "Goth"
},
"grc": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
]
},
"gsw": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Gujr"
},
"guf": {
"sc": "Latn"
},
"gv": {
"haswikt": 1
},
"ha": {
"haswikt": 1
},
"hak": {
"sc": "Hani"
},
"har": {
"sc": "Ethi"
},
"he": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"hi": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Deva"
},
"hif": {
"sc": [
"Latn",
"Deva"
]
},
"hit": {
"sc": "Xsux"
},
"hrx": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"hsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"hsn": {
"sc": "Hani"
},
"hu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"hy": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Armn"
},
"ia": {
"haswikt": 1
},
"id": {
"haswikt": 1
},
"ie": {
"haswikt": 1
},
"ik": {
"haswikt": 1
},
"ike": {
"sc": "Cans"
},
"ikt": {
"sc": "Cans"
},
"io": {
"haswikt": 1
},
"is": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"it": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"iu": {
"haswikt": 1,
"sc": "Cans"
},
"ja": {
"haswikt": 1,
"sc": "Jpan"
},
"jay": {
"sc": "Latn"
},
"jbo": {
"haswikt": 1
},
"jv": {
"haswikt": 1
},
"ka": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"khb": {
"sc": [
"Talu",
"Lana",
"Thai"
]
},
"kjh": {
"sc": "Cyrl"
},
"kk": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"kkh": {
"sc": [
"Lana",
"Thai"
]
},
"kl": {
"haswikt": 1
},
"km": {
"haswikt": 1,
"sc": "Khmr"
},
"kn": {
"haswikt": 1,
"sc": "Knda"
},
"ku": {
"g": [
"n",
"m",
"mnmane",
"mndudil",
"n-p",
"m-p",
"p"
],
"wiktprefix": "ku"
},
"ko": {
"haswikt": 1,
"sc": "Kore"
},
"krc": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"ks": {
"haswikt": 1,
"sc": [
"Arab",
"Deva"
],
"wsc": "ks-Arab"
},
"kw": {
"haswikt": 1
},
"ky": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"la": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"lb": {
"g": [
"n",
"m",
"nt",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"lez": {
"sc": "Cyrl"
},
"li": {
"haswikt": 1
},
"lki": {
"wiktprefix": "ku"
},
"lld": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lmo": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ln": {
"haswikt": 1
},
"lo": {
"haswikt": 1,
"sc": "Laoo"
},
"lt": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lv": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lzz": {
"sc": "Geor"
},
"mg": {
"haswikt": 1
},
"mh": {
"haswikt": 1
},
"mi": {
"g": 0,
"haswikt": 1
},
"mk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ml": {
"haswikt": 1,
"sc": "Mlym"
},
"mn": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Cyrl",
"Mong"
]
},
"mnp": {
"sc": "Hani"
},
"mo": {
"haswikt": 1,
"sc": "Cyrl"
},
"mol": {
"sc": "Cyrl"
},
"mr": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1,
"sc": "Deva"
},
"ms": {
"haswikt": 1,
"sc": [
"Latn",
"Arab"
]
},
"mt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"mwp": {
"sc": "Latn"
},
"my": {
"haswikt": 1,
"sc": "Mymr"
},
"na": {
"haswikt": 1
},
"nah": {
"haswikt": 1
},
"nan": {
"wiktprefix": "zh-min-nan",
"sc": "Hani"
},
"nb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"wiktprefix": "no"
},
"nds-de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"nds-nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"ne": {
"haswikt": 1,
"sc": "Deva"
},
"nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nn": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"no": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nod": {
"sc": "Lana"
},
"non": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"oc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"om": {
"haswikt": 1
},
"or": {
"haswikt": 1,
"sc": "Orya"
},
"orv": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrs"
},
"osc": {
"sc": "Ital"
},
"ota": {
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "ota-Arab"
},
"pa": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": [
"Guru",
"Arab"
]
},
"pdt": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"peo": {
"sc": "Xpeo"
},
"phn": {
"sc": "Phnx"
},
"pi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"pjt": {
"sc": "Latn"
},
"pl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"pms": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"pox": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"împf",
"pf"
]
},
"prs": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"ps": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ps-Arab"
},
"pt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"qu": {
"haswikt": 1
},
"rgn": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"rit": {
"sc": "Latn"
},
"rm": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"rn": {
"haswikt": 1
},
"ro": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"roa-rup": {
"haswikt": 1
},
"ru": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"rue": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrl"
},
"ruo": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rup": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "roa-rup"
},
"ruq": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rw": {
"haswikt": 1
},
"sa": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Deva"
},
"sah": {
"sc": "Cyrl"
},
"sc": {
"haswikt": 1
},
"scn": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"sco": {
"sc": "Latn"
},
"sd": {
"haswikt": 1,
"sc": "Arab",
"wsc": "sd-Arab"
},
"sdh": {
"sc": "Arab",
"wiktprefix": "ku"
},
"sg": {
"haswikt": 1
},
"sh": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"si": {
"haswikt": 1,
"sc": "Sinh"
},
"simple": {
"haswikt": 1
},
"sk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sl": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sm": {
"haswikt": 1
},
"sn": {
"haswikt": 1
},
"so": {
"haswikt": 1
},
"spx": {
"sc": "Ital"
},
"sq": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ss": {
"haswikt": 1
},
"st": {
"haswikt": 1
},
"su": {
"haswikt": 1
},
"sux": {
"sc": "Xsux"
},
"sv": {
"g": [
"g",
"nt",
"g-p",
"nt-p"
],
"haswikt": 1
},
"sva": {
"sc": "Geor"
},
"sw": {
"nclass": 1,
"haswikt": 1
},
"syc": {
"sc": "Syrc"
},
"syr": {
"sc": "Syrc"
},
"szl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"ta": {
"haswikt": 1,
"sc": "Taml"
},
"tdd": {
"sc": "Tale"
},
"te": {
"haswikt": 1,
"sc": "Telu"
},
"tg": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Cyrl"
},
"th": {
"haswikt": 1,
"sc": "Thai"
},
"ti": {
"haswikt": 1,
"sc": "Ethi"
},
"tig": {
"sc": "Ethi"
},
"tiw": {
"sc": "Latn"
},
"tk": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tl": {
"haswikt": 1,
"sc": [
"Latn",
"Tglg"
]
},
"tmr": {
"sc": "Hebr"
},
"tn": {
"haswikt": 1
},
"to": {
"haswikt": 1
},
"tpi": {
"haswikt": 1
},
"tr": {
"g": [
"y",
"p"
],
"alt": 1,
"haswikt": 1
},
"ts": {
"haswikt": 1
},
"tt": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tts": {
"sc": "Thai"
},
"tw": {
"haswikt": 1
},
"udi": {
"sc": [
"Cyrl",
"Latn",
"Armn",
"Geor"
]
},
"ug": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ug-Arab"
},
"uga": {
"sc": "Ugar"
},
"uk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ulk": {
"sc": "Latn"
},
"ur": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ur-Arab"
},
"uz": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"vec": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"vi": {
"haswikt": 1
},
"vls": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"vo": {
"haswikt": 1
},
"wa": {
"haswikt": 1
},
"wbp": {
"sc": "Latn"
},
"wo": {
"haswikt": 1
},
"wuu": {
"sc": "Hani"
},
"xae": {
"sc": "Ital"
},
"xcr": {
"sc": "Cari"
},
"xfa": {
"sc": "Ital"
},
"xh": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"xlc": {
"sc": "Lyci"
},
"xld": {
"sc": "Lydi"
},
"xlu": {
"sc": "Xsux"
},
"xmf": {
"sc": "Geor"
},
"xno": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"xrr": {
"sc": "Ital"
},
"xst": {
"sc": "Ethi"
},
"xum": {
"sc": "Ital"
},
"xve": {
"sc": "Ital"
},
"xvo": {
"sc": "Ital"
},
"yi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"yo": {
"haswikt": 1
},
"yua": {
"g": [
"y",
"p"
],
"alt": 1
},
"yue": {
"sc": "Hani"
},
"za": {
"haswikt": 1,
"sc": "Latn"
},
"zh-classical": {
"sc": "Hani"
},
"zh-min-nan": {
"haswikt": 1
},
"zh-yue": {
"sc": "Hani"
},
"zu": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"zza": {
"g": [
"n",
"m",
"mn",
"n-p",
"m-p",
"p"
],
"wiktprefix": "diq"
}
},
"ziman": {
"ku": "kurmancî",
"en": "îngilîzî",
"tr": "tirkî",
"de": "almanî",
"pl": "polonî",
"ja": "japonî",
"fa": "farisî",
"sh": "sirboxirwatî",
"fr": "fransî",
"ka": "gurcî",
"it": "îtalî",
"fi": "fînî",
"ru": "rusî",
"es": "spanî",
"ar": "erebî",
"nl": "holendî",
"hi": "hindî",
"cs": "çekî",
"cmn": "mandarînî",
"pt": "portugalî",
"uk": "ûkraynî",
"hu": "mecarî",
"ko": "koreyî",
"be": "belarusî",
"la": "latînî",
"ckb": "soranî",
"sv": "swêdî",
"hy": "ermenî",
"bg": "bulgarî",
"ro": "romanyayî",
"da": "danmarkî",
"gl": "galîsî",
"el": "yûnanî",
"eo": "esperantoyî",
"tl": "tagalogî",
"fo": "ferî",
"vi": "viyetnamî",
"ca": "katalanî",
"is": "îzlendî",
"et": "estonî",
"az": "azerî",
"ceb": "sebwanoyî",
"mk": "makedonî",
"he": "îbranî",
"th": "tayî",
"lv": "latviyayî",
"sk": "slovakî",
"ms": "malezî",
"ga": "îrlendî",
"tg": "tacikî",
"grc": "yûnaniya kevn",
"sl": "slovenî",
"kk": "qazaxî",
"mi": "maorî",
"mn": "mongolî",
"sq": "albanî",
"uz": "ozbekî",
"lt": "lîtwanî",
"ur": "urdûyî",
"km": "ximêrî",
"id": "endonezyayî",
"cy": "weylsî",
"lo": "lawsî",
"bn": "bengalî",
"zza": "zazakî",
"ky": "kirgizî",
"yi": "yidîşî",
"gd": "gaelîka skotî",
"my": "burmayî",
"te": "telûgûyî",
"nb": "norweciya bokmålî",
"zh": "çînî",
"ps": "peştûyî",
"eu": "baskî",
"oc": "oksîtanî",
"sw": "swahîlî",
"ast": "astûrî",
"af": "afrîkansî",
"tk": "tirkmenî",
"nn": "norweciya nînorskî",
"yue": "kantonî",
"ug": "oygurî",
"rm": "romancî",
"sc": "sardînî",
"vo": "volapûkî",
"io": "îdoyî",
"ml": "malayalamî",
"mt": "maltayî",
"ba": "başkîrî",
"ta": "tamîlî",
"ang": "îngilîziya kevn",
"sa": "sanskrîtî",
"tt": "teterî",
"rup": "aromanî",
"nrf": "normandî",
"no": "norwecî",
"scn": "sicîlî",
"br": "bretonî",
"as": "asamî",
"bo": "tîbetî",
"nan": "minnanî",
"nv": "navajoyî",
"fy": "frîsî",
"pa": "puncabî",
"vec": "venîsî",
"fur": "friyolî",
"wa": "walonî",
"haw": "hawayî",
"lb": "luksembûrgî",
"am": "amharî",
"arz": "erebiya misrî",
"ia": "înterlîngua",
"kl": "kalalîsûtî",
"mr": "maratî",
"gv": "manksî",
"qu": "keçwayî",
"dsb": "sorbiya jêrîn",
"si": "kîngalî",
"kn": "kannadayî",
"sdh": "kurdiya başûrî",
"ota": "osmanî",
"gu": "gujaratî",
"ce": "çeçenî",
"jv": "javayî",
"pdt": "plodîşî",
"chr": "çerokî",
"nds": "nedersaksî",
"acw": "erebiya hîcazî",
"cu": "slaviya kevn",
"ne": "nepalî",
"kw": "kornî",
"hsb": "sorbiya jorîn",
"za": "zhuangî",
"os": "osetî",
"se": "samiya bakurî",
"dng": "dunganî",
"izh": "îngriyî",
"sco": "skotî",
"got": "gotî",
"or": "oriyayî",
"mnc": "mançûyî",
"crh": "teteriya krîmî",
"ltg": "latgalî",
"sah": "yakutî",
"ht": "haîtî",
"ary": "erebiya fasî",
"ha": "hawsayî",
"zu": "zuluyî",
"cv": "çuvaşî",
"yo": "yorubayî",
"ab": "abxazî",
"so": "somalî",
"frr": "frîsiya bakur",
"lad": "ladînoyî",
"an": "aragonî",
"ti": "tigrînî",
"aii": "aramiya nû ya asûrî",
"hak": "hakkayî",
"enm": "îngilîziya navîn",
"pi": "palî",
"navz": "navzimanî",
"ny": "çîçewayî",
"om": "oromoyî",
"nds-de": "almaniya jêrîn a almanî",
"lmo": "lombardî",
"csb": "kaşûbî",
"alt": "altayiya başûrî",
"arc": "aramî",
"bcl": "bîkoliya naverast",
"co": "korsîkayî",
"oj": "ojibweyî",
"pap": "papyamentoyî",
"rue": "rusînî",
"av": "avarî",
"txb": "toxarî B",
"xal": "kalmîkî",
"cop": "qiptî",
"mg": "malagasî",
"nap": "napolîtanî",
"gsw": "elmanîşî",
"dlm": "dalmatî",
"gn": "guwaranî",
"orv": "slaviya rojavayî ya kevn",
"tyv": "tuvanî",
"dv": "divehî",
"ady": "adîgeyî",
"bua": "buryatî",
"wuu": "wuyî",
"pam": "kapampanganî",
"myv": "erzayî",
"udm": "udmurtî",
"khb": "lueyî",
"rom": "romanî",
"su": "sundanî",
"ilo": "îlokanoyî",
"syc": "siryaniya klasîk",
"zdj": "şîngazîcayî",
"cdo": "min dongî",
"evn": "evenkî",
"kum": "kumikî",
"bal": "belûçî",
"lld": "ladînî",
"inh": "îngûşî",
"sd": "sindhî",
"lez": "lezgînî",
"fro": "fransiya kevn",
"non": "norsiya kevn",
"liv": "lîvonî",
"tet": "tetûmî",
"tpi": "tokpisinî",
"shn": "şanî",
"wo": "wolofî",
"mrw": "maranaoyî",
"gld": "nanayî",
"nah": "nahwatlî",
"ki": "kîkûyûyî",
"st": "sotoyiya başûr",
"mww": "hmongî",
"lij": "lîgûrî",
"rgn": "romagnolî",
"li": "lîmbûrgî",
"sm": "samoayî",
"sga": "îrlendiya kevn",
"ks": "keşmîrî",
"pms": "piedmontîsî",
"ccc": "çamicuroyî",
"hil": "hîlîgaynonî",
"xmf": "megrelî",
"hrx": "hunsrikî",
"rif": "tarifitî",
"krc": "karaçay-balkarî",
"war": "warayî",
"mwl": "mîrandî",
"syl": "sîlhetî",
"ee": "eweyî",
"ln": "lingalayî",
"pdc": "almaniya Pennsylvania",
"goh": "almaniya bilind a kevn",
"tly": "talişî",
"nci": "nahwatliya klasîk",
"ckt": "çukçî",
"ain": "aynuyî",
"uga": "ugarîtî",
"xh": "xosayî",
"sms": "samiya skoltî",
"kea": "kapverdî",
"ajp": "erebiya başûrê şamê",
"kjh": "xakasî",
"iu": "înuîtî",
"dz": "conxayî",
"srn": "sirananî",
"vro": "voroyî",
"mdf": "mokşayî",
"xcl": "ermeniya kevn",
"mhr": "mariya rojhilatî",
"cic": "çîkasawî",
"esu": "yupîkiya navendî",
"cjs": "şorî",
"wym": "wîlamowî",
"apw": "apaçiya rojava",
"mba": "higaononî",
"kaw": "javayiya kevn",
"ovd": "elfdalî",
"to": "tongî",
"ist": "îstriyotî",
"szl": "silesî",
"yua": "mayayiya yukatekî",
"tsg": "tausugî",
"zea": "zêlandî",
"bar": "bayerî",
"luy": "luhyayî",
"mzn": "mazenderanî",
"mhn": "mòçenoyî",
"kxd": "bruneyî",
"lzz": "lazî",
"kbd": "kabardî",
"afb": "erebiya kendavî",
"krl": "karelî",
"eve": "evenî",
"stq": "frîsiya saterlandî",
"ak": "akanî",
"tab": "tabasaranî",
"sat": "santalî",
"fj": "fîjî",
"sva": "svanî",
"egy": "misrî",
"ig": "îgboyî",
"hac": "hewramî",
"ay": "aymarayî",
"gur": "farefareyî",
"rw": "kînyarwandayî",
"twf": "taosî",
"koi": "komî-permyakî",
"lkt": "lakotayî",
"akl": "aklanon",
"yai": "yagnobî",
"ryu": "okînawanî",
"rhg": "rohingyayî",
"vep": "vepsî",
"smn": "samiya înarî",
"egl": "emilî",
"ty": "tahîtî",
"mdh": "maguindanaoyî",
"gag": "gagawzî",
"tzm": "temazîxtiya atlasa navendî",
"sgs": "samogîtî",
"ff": "fulahî",
"lou": "kreyoliya fransî ya Louisiana",
"mel": "melanauyiya navendî",
"niv": "nîvxî",
"aa": "afarî",
"gez": "etiyopiya klasîk",
"ext": "extremaduranî",
"frp": "frankoprovansî",
"kpv": "komî-zîriyî",
"udi": "udî",
"sma": "samiya başûr",
"nog": "nogayî",
"akk": "akadî",
"lbe": "lakî",
"min": "mînangkabawî",
"kg": "kîkongoyî",
"ban": "balînî",
"dlg": "dolganî",
"ace": "açehî",
"pox": "polabî",
"ppl": "pîpîlî",
"mnz": "monî",
"kaa": "karakalpakî",
"kab": "kabîlî",
"vot": "votî",
"luo": "luoyî",
"sn": "şonayî",
"swb": "komorî",
"roa-tar": "tarantinoyî",
"lg": "lugandayî",
"nds-nl": "saksoniya jêrîn ya holendî",
"dtp": "dusuniya navendî",
"shi": "taşelhitî",
"msk": "mansakayî",
"rap": "rapanuyî",
"mer": "meruyî",
"ik": "înupîakî",
"prg": "prusiya kevn",
"chk": "çûkî",
"kam": "kambayî",
"chy": "çeyenî",
"nod": "tayiya bakurî",
"akz": "alabamayî",
"xag": "aghwanî",
"ase": "zimanê destan ê emerîkî",
"nmn": "ǃxóõ",
"gun": "mbya guaraniyî",
"cr": "krî",
"yag": "yamanayî",
"otk": "tirkiya kevn",
"sid": "sidamoyî",
"tum": "tumbukayî",
"pjt": "pîttcantcatcarayî",
"sux": "sûmerî",
"bbl": "batsbî",
"jje": "jejuyî",
"bm": "bambarayî",
"frm": "fransiya navîn",
"tpn": "tupînambayî",
"pal": "farisiya navîn",
"bi": "bîslamayî",
"ksw": "s'gaw karenî",
"ssy": "sahoyî",
"bdr": "bajwayiya kendava rojava",
"sne": "bau bidayuh",
"sjd": "samiya kildînî",
"na": "nawrûyî",
"pag": "pangasînanî",
"abq": "abazayî",
"tn": "tswanayî",
"osx": "saksoniya kevn",
"aqc": "arçî",
"xsr": "şerpayî",
"kok": "konkanî",
"kln": "kalencînî",
"win": "wînnebagoyî",
"dar": "dargwayî",
"mfe": "kreyoliya morîtanî",
"iba": "îbanî",
"ae": "avestayî",
"mos": "moreyî",
"arn": "mapuçî",
"ill": "îranunî",
"mrj": "mariya rojavayî",
"cst": "ohloniya bakur",
"blt": "tay dam",
"apc": "erebiya bakûrê şamê",
"gan": "ganî",
"ii": "yiyiya siçuwayî",
"xto": "toxarî A",
"wmw": "mwanî",
"avd": "elwîrî-wîderî",
"pro": "oksîtaniya kevn",
"pcc": "buyî",
"jra": "jarayî",
"new": "newarî",
"unm": "unamî",
"mic": "mîkmakî",
"cjm": "çamiya rojhilatî",
"sdn": "galûrîsî",
"lus": "mizoyî",
"cjy": "jinî",
"tli": "tlingitî",
"bug": "bugî",
"pcd": "pîkardî",
"hop": "hopîyî",
"ofs": "frîsiya kevn",
"tft": "ternateyî",
"roa-opt": "portugaliya kevn",
"gil": "kîrîbatî",
"kac": "cingphoyî",
"csm": "sierra mîwokiya navendî",
"sdc": "sassarîsî",
"ood": "o'odhamî",
"hsn": "xiangî",
"ruo": "îstro-romanyayî",
"mbb": "bukidnon manoboyiya rojavayî",
"bho": "bojpûrî",
"ani": "andî",
"kgp": "kayingangî",
"ss": "swazî",
"mnp": "min beî",
"aho": "ahomî",
"moe": "montagnaîsî",
"naq": "namayî",
"mvi": "miyakoyî",
"duu": "drungî",
"lut": "luşûtsîdî",
"sce": "dongşîangî",
"bqi": "bextiyarî",
"mrc": "marîkopayî",
"gmh": "almaniya bilind a navîn",
"cim": "cimbrî",
"aer": "arrernteyiya rojavayî",
"agj": "argobbayî",
"ium": "îu mienî",
"tay": "atayalî",
"ale": "alêwîtî",
"str": "saanîçî",
"zku": "kaurnayî",
"pnt": "yûnaniya pontî",
"gmw-cfr": "frankoniya navendî",
"bxd": "pelayî",
"ems": "alutîqî",
"mtq": "muongî",
"hit": "hîtîtî",
"mak": "makasarî",
"bjn": "banjarî",
"nay": "ngarrindjerî",
"dak": "dakotayî",
"yrk": "nenetsiya tundra",
"pac": "pacohî",
"ch": "çamoroyî",
"vls": "flamiya rojava",
"acm": "erebiya iraqî",
"smj": "samiya lule",
"lmy": "lamboyayî",
"jam": "kreyoliya jameîkî",
"yka": "yakanî",
"gmw-ecg": "almaniya navendî ya rojhilat",
"xsv": "sûdovî",
"cdm": "çepangî",
"bdk": "buduxî",
"isd": "îsnagî",
"zgh": "amazîxiya fasî",
"css": "ohloniya başûr",
"kqr": "kîmaragangî",
"glk": "gîlekî",
"aeb": "erebiya tunusî",
"lki": "lekî",
"kdr": "karaîmî",
"kld": "gamîlaraayî",
"tdd": "tayî nüa",
"xqa": "karaxanîdî",
"nso": "sotoyiya bakur",
"crs": "kreyoliya seyşelî",
"mvv": "tagal murutî",
"kim": "tofalarî",
"swg": "swabî",
"pau": "palawî",
"wrh": "wiradhurî",
"cho": "çoktawî",
"tig": "tigreyî",
"ivv": "îvatanî",
"ddg": "fatalukuyî",
"rmf": "romaniya kalo fînî",
"pih": "pîtkarnî-norfolkî",
"phn": "fenîkî",
"ktu": "kitubayî",
"mwr": "marwarî",
"wbp": "warlpirî",
"zun": "zuniyî",
"crk": "kriya bejayî",
"ebk": "bontociya rojhilatî",
"nup": "nupeyî",
"yoi": "yonagunî",
"atj": "atîkamekwî",
"duo": "dupaningan agtayî",
"kca": "xantî",
"dta": "daurî",
"rar": "rarotongî",
"abe": "abenakî",
"aot": "atong (Hindistan)",
"osa": "osageyî",
"ami": "amîs",
"sea": "semaî",
"mh": "marşalî",
"mad": "madurayî",
"sei": "sêriyî",
"moh": "mohawkî",
"meu": "hîrî motuyî",
"pwn": "paîwanî",
"okm": "koreyiya navîn",
"xug": "kunigamî",
"amu": "amuzgoyî",
"ine-pro": "proto-hindûewropî",
"tzo": "tzotzîlî",
"ksc": "kalingayiya başûrî",
"sib": "sebopî",
"uum": "ûrûmî",
"kjj": "khinalugî",
"pis": "pijînî",
"peo": "farisiya kevn",
"sg": "sangoyî",
"tyz": "tàyî",
"kpy": "koryakî",
"ruq": "megleno-romanyayî",
"abs": "ambonezî-malezî",
"mas": "masayî",
"bdq": "bahnarî",
"mkz": "makasaeyî",
"mag": "magahî",
"oaa": "orokî",
"din": "dinkayî",
"zlw-slv": "slovînsî",
"olo": "livvî",
"mai": "maithilî",
"nhn": "nahwatliya navendî",
"nan-tws": "teoçewî",
"wuh": "wutunhuayî",
"tpw": "tûpiya kevn",
"gcf": "kreyoliya antîlleyî",
"knb": "lubuagan kalingayî",
"ket": "ketî",
"pua": "purepeçayî",
"xbr": "kamberayî",
"bor": "bororoyî",
"rmy": "romaniya vlaks",
"tao": "yamî",
"arq": "erebiya cezayirî",
"brh": "brahuiyî",
"chn": "çinook jargonî",
"cbk": "çavacanoyî",
"tvo": "tidoreyî",
"wbk": "waigalî",
"nio": "nganasanî",
"bsh": "kamkata-viriyî",
"roa-gal": "galoyî",
"krj": "kinaray-ayî",
"gmy": "yûnaniya mîkenî",
"ctd": "tedim çinî",
"ude": "udiheyî",
"gmw-rfr": "frankoniya rhine",
"tvl": "tuvalûyî",
"ibg": "îbanagî",
"ask": "aşkunî",
"ccp": "çakmayî",
"ts": "tsongayî",
"sje": "pite samî",
"esh": "iştihardî",
"quc": "k'içe'",
"ho": "hirî motuyî",
"hif": "fiji hindiyî",
"orh": "oroqenî",
"ett": "etrûskî",
"zai": "îsthmus zapotekî",
"bla": "blakfotî",
"zlw-opl": "poloniya kevn",
"ses": "sonxayî",
"lis": "lisuyî",
"kr": "kanuriyî",
"bbc": "bataktobayî",
"umb": "umbunduyî",
"rys": "yaeyamayî",
"arp": "arapaho",
"bol": "boleyî",
"com": "komançî",
"ifk": "tuwali ifugaoyî",
"ve": "vendayî",
"kri": "krioyî",
"aoz": "uab meto",
"yxg": "yagarayî",
"zom": "zouyî",
"ttt": "tatî",
"ams": "amamî-oşîmayiya başûrî",
"tts": "isanî",
"igl": "îgalayî",
"xda": "darkinjungî",
"fkv": "kvenî",
"chg": "çaxatayî",
"xwo": "oyratiya nivîsî",
"cab": "garîfunayî",
"atv": "altayiya bakurî",
"tiw": "tiwî",
"fit": "meänkiyelî",
"mnk": "mandinkayî",
"yox": "yoronî",
"tfn": "dena'inayî",
"hur": "halkomelemî",
"ryn": "amamî-oşimayiya bakurî",
"rop": "kriolî",
"ckv": "kavalanî",
"ulc": "ulçî",
"hts": "hadzayî",
"anq": "jarawa",
"dgr": "dogrîbî",
"myp": "pîrahayî",
"xfa": "faliskanî",
"gin": "hinukhî",
"pqm": "malecite-passamaquoddyî",
"mni": "manipurî",
"txg": "tangutî",
"trv": "tarokoyî",
"kha": "khasiyî",
"odt": "holendiya kevn",
"tby": "tabaruyî",
"drg": "rungusî",
"yur": "yurokî",
"neg": "negidalî",
"mga": "îrlendiya navîn",
"gor": "gorontaloyî",
"tcy": "tuluyî",
"oac": "oroçî",
"nia": "nîasî",
"skb": "saekî",
"bth": "biatah bidayuhî",
"nwy": "nottoway-meherrînî",
"chc": "katawbayî",
"bgc": "haryanvî",
"tih": "timugon murutî",
"itl": "îtelmenî",
"gml": "almaniya jêrîn a navîn",
"apj": "xîkarîyayî",
"yao": "yaoyî",
"okn": "oki-no-erabu",
"juc": "jurçenî",
"bnn": "bununî",
"nmb": "nambasiya mezin",
"cuk": "kunayî",
"uby": "ûbîkî",
"bpy": "manîpûriya bîşnûpriyayî",
"alr": "alutor",
"nys": "nyungayî",
"niq": "nandî",
"nnt": "nanticokeyî",
"shy": "şawî",
"xum": "umbriyî",
"kzg": "kikaiyî",
"blk": "pa'o karen",
"khi-kun": "ǃkungî",
"amw": "neo-aramiya rojavayî",
"kyh": "karokî",
"arw": "arawak",
"rkt": "kamtayî",
"ijc": "îzonî",
"jct": "krymçakî",
"xpq": "mohegan-pequotî",
"lnd": "lundayehî",
"xnb": "kanakanabuyî",
"niu": "nîwî",
"wls": "walîsî",
"axm": "ermeniya navîn",
"kap": "bezhtayî",
"ykg": "yukaghiriya bakurî",
"kky": "guugu yimidhirrî",
"hnj": "hmongiya hêşîn",
"dtr": "lotudî",
"kla": "klamatî-modokî",
"fax": "falayî",
"sjt": "ter samî",
"nxq": "naxî",
"oge": "gurciya kevn",
"apm": "çîrîkawayî",
"tzj": "tzutucîlî",
"tbl": "tbolî",
"inc-oas": "asamiya zû",
"hai": "haidayî",
"sou": "taiya başûrî",
"osc": "oskanî",
"shh": "şoşonî",
"xmz": "morî bawah",
"sju": "ume samî",
"xpr": "partî",
"gaa": "gayî",
"nch": "nahwatliya huastecaya navendî",
"bsk": "buruşaskiyî",
"bzj": "kreyoliya belizeyî",
"nhx": "nahwatliya mekayapan",
"ktb": "kambaatayî",
"xnn": "kankanayiya bakurî",
"pot": "potawatomî",
"agr": "aguarunayî",
"bsb": "bisayayiya bruneyî",
"cja": "çamiya rojavayî",
"nov": "noviyalî",
"kbp": "kabiyeyî",
"tkn": "toku-no-şîmayî",
"lzh": "çîniya klasîk",
"caa": "ç'orti'",
"paw": "pawnî",
"wbl": "waxiyî",
"rut": "rutulî",
"lud": "ludianî",
"gon": "gondiyî",
"lsd": "lîşana denî",
"pyu": "puyumayî",
"bej": "bejayî",
"sxr": "saaroayî",
"bsq": "bassayî",
"cmg": "mongoliya klasîk",
"mus": "krîkî",
"sas": "sasakî",
"mlm": "mulamî",
"aio": "aytonî",
"nuk": "nootkayî",
"aiw": "aarî",
"enq": "engayî",
"chl": "cahuillayî",
"huz": "hunzibî",
"grt": "garoyî",
"lic": "hlaiyî",
"fia": "nobiinî",
"osp": "spaniya kevn",
"amm": "amayî",
"ote": "otomiya mezquital",
"roa-leo": "leonîzî",
"dum": "holendiya navîn",
"awa": "awadhî",
"otw": "ottawayî",
"xbc": "baxterî",
"bem": "bembayî",
"ira-pro": "proto-îranî",
"dbl": "diyirbalî",
"mcm": "kristangî",
"ctu": "çolî",
"grk-mar": "yûnaniya mariupol",
"nst": "tangsayî",
"cak": "kaqçikelî",
"pov": "kreyoliya guinea-bissau",
"mbf": "malayiya baba",
"xsy": "saisiyatî",
"bno": "asî",
"lbj": "ladakhiyî",
"adt": "adnyamatanhayî",
"ttj": "tooroyî",
"apy": "apalayî",
"pim": "powhatanî",
"roa-brg": "bûrgûndî",
"szy": "sakizayayî",
"tkl": "tokelawî",
"wlm": "weylsiya navîn",
"gdo": "godoberî",
"ayn": "erebiya sanaanî",
"ksk": "kansayî",
"aht": "ahtnayî",
"btx": "batakiya karo",
"jdt": "tatiya cihû",
"cni": "aşanînkayî",
"skr": "seraikî",
"iir-pro": "proto-hindûîranî",
"yrl": "nhêngatûyî",
"sip": "sîkkîmî",
"ie": "înterlîngue",
"bin": "edoyî",
"sac": "foksî",
"wni": "komoriya ndzwanî",
"btq": "batekî",
"bkd": "binukid",
"gmq-scy": "skanî",
"rah": "rabhayî",
"ddo": "tsezî",
"tuw-sol": "solonî",
"gni": "gooniyandiyî",
"rmn": "romaniya balkanî",
"roa-poi": "poitevin-saintongeaisî",
"mqm": "marquesana başûr",
"tsu": "tsouyî",
"yol": "yolayî",
"fud": "futunî",
"umu": "munsî",
"wam": "massaçusettî",
"bsy": "sabah bisayayî",
"prn": "prasunî",
"clm": "klallamî",
"ssf": "thaoyî",
"jbo": "lojbanî",
"nus": "nuerî",
"mhx": "lhao voyî",
"hmc": "huişui hmongiya navendî",
"lrc": "luriya bakurî",
"tiy": "tirurayî",
"kkh": "khünî",
"nfr": "nafaanrayî",
"zpq": "zapotekiya zoogoço",
"suk": "sukumayî",
"rml": "romaniya baltîkî",
"ncj": "nahwatliya bakurê puebla",
"nhg": "nahwatliya tetelcingo",
"ars": "erebiya necdî",
"bh": "biharî",
"caq": "car nicobareseyî",
"wlc": "mwalî komorî",
"apd": "erebiya sudanî",
"mop": "mayayiya mopanî",
"yaq": "yaquî",
"pon": "ponpeyî",
"kwk": "kwak'walayî",
"wlo": "wolioyî",
"nnr": "narunggayî",
"agx": "aghulî",
"dty": "doteliyî",
"ybh": "yakkhayî",
"azz": "puebla nahuatliya çiyayî",
"yuy": "yuguriya rojhilat",
"tqw": "tonkawayî",
"hss": "harsusiyî",
"mqu": "mandarî",
"maz": "mazahuayiya navendî",
"ife": "îfèî",
"kmk": "limos kalingayî",
"shs": "şuswapî",
"hid": "hidatsayî",
"brg": "baureyî",
"wrr": "wardamanî",
"kis": "kîsî",
"acy": "erebiya qibrisî",
"tin": "tindî",
"gmq-osw": "swêdiya kevn",
"yuf": "havasupaî-walapaî-yavapaî",
"xdk": "darûkî",
"wau": "waujayî",
"ljp": "lampungî",
"alu": "areareyî",
"ach": "açolî",
"bkm": "komiya kamerûnî",
"ngn": "ngwoyî",
"bsa": "abînomnî",
"der": "deoriyî",
"shp": "şipibo-coniboyî",
"ycl": "lolopoyî",
"qua": "kuapawî",
"gmq-pro": "proto-norsî",
"nnv": "nukunuyî",
"zko": "kottî",
"khv": "khvarşiyî",
"nij": "ngajuyî",
"yog": "yogadî",
"raw": "rawangî",
"mfy": "mayoyî",
"nd": "ndebeliya bakurî",
"oko": "koreyiya kevn",
"fon": "fonî",
"abx": "abaknonî",
"rad": "radeyî",
"klj": "khalajî",
"tru": "turoyo",
"nag": "naga pidginî",
"crl": "kriya rojhilat ya bakurî",
"zlw-ocs": "çekiya kevn",
"aau": "abauyî",
"mrq": "marquesana bakur",
"sia": "samiya akkalayî",
"mjg-mgl": "mongghulî",
"car": "galîbî karîbî",
"roa-tou": "torangî",
"rel": "rendilleyî",
"oma": "omaha-poncayî",
"rmq": "kaloyî",
"rmo": "romaniya sinte",
"lep": "lepçayî",
"guw": "gunî",
"knj": "akatekî",
"slr": "salarî",
"sem-srb": "erebiya başûrî ya kevn",
"pad": "paumarî",
"yap": "yapî",
"xul": "ngunawalî",
"kzj": "kadazaniya beravî",
"fan": "fangî (Bantu)",
"brx": "bodoyî (Hindistan)",
"ane": "xârâcùù",
"cro": "crowî",
"ram": "kanelayî",
"tea": "temiarî",
"cnk": "khumi çinî",
"tkr": "tsakhurî",
"ulk": "meryamî",
"tzh": "tzeltalî",
"avk": "kotavayî",
"mjg-mgr": "mangghuerî",
"xav": "xavanteyî",
"kmv": "kreyoliya fransî ya karipúna",
"aas": "aasayî",
"guz": "gusiiyî",
"erk": "efateyiya başûr",
"ngh": "nlûyî",
"cel-gau": "gaulisî",
"hz": "hereroyî",
"alq": "algonquinî",
"cay": "cayugayî",
"inz": "înesenyoyî",
"sgr": "sangisarî",
"iro-min": "mingoyî",
"dru": "rukaiyî",
"tta": "tuteloyî",
"lui": "luiseñoyî",
"mgm": "mambayî",
"xpm": "pumpokolî",
"kyj": "karaoyî",
"ksd": "tolaiyî",
"pkc": "baekceyî",
"pcm": "pidgîniya nîjeryayî",
"kjg": "khmuyî",
"cow": "cowlitzî",
"cyo": "cuyunonî",
"msn": "vuresî",
"jhi": "jehaiyî",
"map-bms": "banyumasanî",
"fla": "montana salişî",
"skd": "sierra miwokiya başûrî",
"xpu": "punîkî",
"tsd": "tsakonî",
"gue": "gurindjiyî",
"ksi": "krisayî",
"pni": "aohengî",
"par": "panamintî",
"ulb": "ulukwumî",
"miq": "miskitoyî",
"mcb": "maçiguengayî",
"kls": "kalaşayî",
"sdo": "bukar-sadung bidayuhî",
"nr": "ndebeliya başûrî",
"mch": "maquiritarî",
"msb": "masbatenyoyî",
"tsi": "tsimşî",
"gmq-gut": "gutnişî",
"tus": "tuskarorayî",
"lmw": "miwokiya golî",
"srs": "sarceeyî",
"zag": "zaghawayî",
"klb": "kiliwayî",
"kmc": "kamiya başûrî",
"beg": "belaîtî",
"inc-ash": "prakrîtiya aşokanît",
"kj": "kwanyamayî",
"ng": "ndongayî",
"irk": "îraqwî",
"grk-ita": "grîkoyî",
"aaq": "penobskotî",
"kek": "q'eqçiyî",
"ojb": "ojibweyiya bakurî",
"txu": "kayapó",
"one": "oneidayî",
"ktv": "katuyiya rojhilatî",
"nhe": "nahwatliya huastekaya rojhilatî",
"lwl": "lawayiya rojhilatî",
"lua": "luba-kasayî",
"bjb": "barngarla",
"acu": "açuarî",
"cof": "tsafikiyî",
"gwi": "gwiçinî",
"urk": "urak lawoi'",
"tar": "tarahumarayiya navendî",
"kva": "bagvalalî",
"hni": "haniyî",
"sek": "sekanî",
"cpg": "yûnaniya kapadokyayî",
"wmb": "wambayayî",
"xld": "lîdyayî",
"amn": "amanabî",
"haa": "hänî",
"cdy": "çadongî",
"akr": "arakî",
"kju": "kaşayayî",
"ada": "adangmeyî",
"alh": "alawayî",
"gdq": "mehriyî",
"tmo": "temoqî",
"kus": "kusaalî",
"dwu": "dhuwalî",
"pak": "parakanã",
"agg": "angorî",
"wao": "wappoyî",
"aji": "ajië",
"cas": "tsimaneyî",
"byq": "basayî",
"xls": "lusîtanî",
"gcr": "kreyoliya guwiyanîsî",
"apk": "apaçiya deştan",
"kut": "kutenaiyî",
"gmw-jdt": "danmarkiya jersey",
"hus": "wastekî",
"rej": "rejangî",
"dog": "dogonî",
"roa-lor": "lorrainî",
"emb": "embalohî",
"tnq": "taînoyî",
"crj": "kriya rojhilat ya başûrî",
"nuj": "nyolî (Uganda)",
"gmq-oda": "danmarkiya kevn",
"akv": "axvaxî",
"doi": "dogriyî",
"trp": "kokborokî",
"geh": "hutterisçî",
"gul": "gullahî",
"coo": "comoxî",
"ije": "biseniyî",
"itk": "îtaliya cihû",
"rmc": "romaniya karpatyayî",
"gmw-gts": "gottscheerîşî",
"ttg": "tutongî",
"ngu": "nahwatliya guerrero",
"pse": "malayiya navendî",
"onw": "nubiya kevn",
"qui": "kuwileytî",
"giw": "gelaoyiya spî",
"ojw": "ojibweyiya rojavayî",
"gay": "gayoyî",
"rmw": "romaniya welşî",
"nhw": "nahwatliya huastecaya rojavayî",
"xkl": "bakungî",
"loz": "loziyî",
"yij": "yindcîbarndî",
"kry": "krytsî",
"ple": "palueyî",
"tht": "tahltanî",
"mkq": "bay miwokî",
"sks": "maiayî",
"mhe": "besisî",
"mrv": "mangarevanî",
"nrm": "naromî",
"chp": "çîpewî",
"bll": "biloxî",
"knx": "kendayanî",
"khw": "khowarî",
"dua": "diwalayî",
"roa-cha": "şampanyî",
"gug": "guaraniyiya paraguayanî",
"dsn": "dusnerî",
"tmh": "tuaregî",
"pmf": "pamonayî",
"smq": "samoyî",
"tal": "talî",
"yuc": "yuçî",
"mqy": "manggarayî",
"bts": "batakiya simalungun",
"tos": "totonakiya çiyayî",
"oia": "oyratayî",
"blz": "balantak",
"bjz": "baruga",
"kai": "karekareyî",
"zav": "zapotekiya yatzaçî",
"okd": "okodiayî",
"ndd": "nde-nsele-nta",
"omc": "moçikayî",
"bto": "iriga bikolanoyî",
"okr": "kirikeyî",
"top": "totonakiya papantla",
"xer": "xerenteyî",
"xlc": "lîkî",
"mid": "mandaicî",
"suy": "suyayî",
"chz": "ozumacin çinantecî",
"pah": "tenharimî",
"til": "tillamookî",
"afn": "defakayî",
"guc": "wayuuyî",
"ojp": "japoniya kevn",
"lif": "limbuyî",
"efi": "efikî",
"mek": "mêkêoyî",
"ayl": "erebiya lîbyayî",
"bnt-phu": "phuthî",
"kne": "kankanaeyî",
"ktn": "karitiyanayî",
"abl": "abûngî",
"rtm": "rotumanî",
"wyb": "ngiyambaayî",
"crg": "mîçîfî",
"kxu": "kuiyî (Hindistan)",
"cts": "bicolanoyiya northern catanduanes",
"dje": "zarmayî",
"plw": "palawanoyî",
"xlu": "luwiyanî",
"koy": "koyukonî",
"vmw": "makhuwayî",
"vmf": "frankiya rojhilatî",
"bru": "bruyiya rojhilatî",
"kmb": "kimbunduyî",
"xsa": "sabaeanî",
"tbk": "tagbanwayiya kalamî",
"kzt": "tambunan dusunî",
"xng": "mongoliya navîn",
"acn": "açangî",
"bwx": "bu-nao bunuyî",
"cwg": "çewongî",
"vmb": "mbabaramî",
"ijs": "îjoyiya başûr-rojhilat",
"asb": "assînîboyî",
"nzd": "nzadî",
"itv": "îtawitî",
"lvk": "lavukaleveyî",
"bde": "badeyî",
"tbc": "takyayî",
"aby": "aneme wakeyî",
"urb": "urubú-kaaporî",
"coh": "çiçonyi-çidzihana-çikauma",
"dbj": "ida'anî",
"smk": "bolinaoyî",
"lbx": "lawanganî",
"kfr": "kaççiyî",
"hlt": "nga layî",
"agm": "angaatahayî",
"xpg": "firîgî",
"smr": "simeulueyî",
"vma": "martuthunirayî",
"gub": "guajajarayî",
"chf": "tabasco çontalî",
"mqs": "makyaniya rojava",
"iby": "îbaniyî",
"cng": "qiangiya bakurî",
"jac": "jakaltekî",
"trw": "torwalî",
"lti": "lêtîyî",
"scl": "şinayî",
"gwr": "gwereyî",
"dgc": "casiguran dumagat agtayî",
"btm": "batakiya mandaîlîng",
"puj": "punan tubuyî",
"oka": "okanaganî",
"gek": "gerkayî",
"mat": "matlatzincayî",
"kio": "kiowayî",
"cku": "koasatî",
"kms": "kamasauyî",
"xaa": "erebiya endulusî",
"gut": "maleku jaikayî",
"aou": "a'ou",
"elx": "îlamî",
"peh": "bonanî",
"pgl": "îrlendiya prîmîtîv",
"myx": "masabayî",
"spp": "supyireyî",
"xnt": "narragansettî",
"cad": "caddoyî",
"aap": "pará arárayî",
"rme": "angloromanî",
"ura": "urarinayî",
"mpm": "mixteciya yosondúa",
"poe": "popolocayiya san juan atzingo",
"sgd": "surigaononî",
"kvh": "komodoyî",
"xtc": "kaduglî",
"tjg": "tunjungî",
"azg": "san pedro amuzgos amuzgo",
"uun": "pazehî",
"udl": "uldemeyî",
"skp": "sekapanî",
"isi": "nkem-nkumî",
"wrz": "warrayî",
"its": "îsekiriyî",
"tgb": "tobilungî",
"bdy": "bandjalangî",
"ssb": "samayiya başûrî",
"hnn": "hanunooyî",
"enn": "engenniyî",
"srm": "saramakkanî",
"dmr": "damariya rojhilat",
"djo": "jangkangî",
"kij": "kîlîvîlayî",
"rob": "tae'",
"sad": "sandaweyî",
"har": "harariyî",
"hov": "hovonganî",
"pos": "popolucayiya sayula",
"sit-bok": "bokarî",
"kns": "kensiuyî",
"mbd": "dibabawon manoboyî",
"asu": "tocantins asurînî",
"sjw": "şawneeyî",
"btw": "butuanonî",
"jut": "jutişî",
"bgs": "tagabawayî",
"xsb": "sambalî",
"mkf": "miyayî",
"mrn": "çeke holoyî",
"kui": "kuikúroyî",
"hup": "hupayî",
"abm": "abanyomî",
"ukk": "muak sa-aakî",
"lbk": "bontociya navendî",
"dbp": "duwaiyî",
"jup": "hupdëî",
"cce": "çopiyî",
"muy": "muyangî",
"agt": "agtayiya kagayanî ya navendî",
"sxn": "sangirî",
"brc": "kreyoliya holendî ya berbiceyî",
"yux": "yukaghiriya başûrî",
"zpv": "zapotekiya çiçicapan",
"teo": "atesoyî",
"dif": "dieriyî",
"maq": "mazateciya çiquihuitlan",
"sgh": "şughnî",
"gnc": "guwançî",
"ega": "egayî",
"kbh": "camsayî",
"bni": "bangî",
"tdj": "tajioyî",
"ngi": "ngizimî",
"bue": "beothukî",
"pok": "pokangayî",
"myw": "muyuwî",
"bkn": "bukîtan",
"dyu": "dyulayî",
"asn": "xingú asuriní",
"cme": "cermayî",
"tna": "takanayî",
"mia": "miamî",
"epi": "epieyî",
"mcq": "eseyî",
"kyu": "kayahiya rojavayî",
"sai-pyk": "pykobjê",
"zaa": "zapotekiya sierra de juarez",
"dhv": "dehûyî",
"bxk": "bukusuyî",
"nuz": "nahwatliya tlamacazapa",
"trm": "tregamî",
"cps": "capiznonî",
"slm": "pangutaran samayî",
"cia": "cia-ciayî",
"abv": "erebiya bahranî",
"lod": "berawanî",
"mhj": "mogholî",
"mnv": "rennelleseyî",
"gir": "gelaoyiya sor",
"bfq": "badagayî",
"wyi": "woiwurrungî",
"eya": "eyakî",
"awk": "awabakal",
"zoc": "zoqueyiya copainala",
"smw": "sumbawayî",
"ank": "goemayî",
"mbl": "maxakalî",
"tbd": "kakî aeyî",
"nez": "nez perceyî",
"crn": "corayî",
"sai-tap": "tapayunayî",
"see": "senekayî",
"huv": "huaveyî",
"apn": "apinayé",
"mfa": "malayiya pattani",
"crx": "karriyerî",
"mam": "mamî",
"ibl": "îbaloiyî",
"cac": "çujî",
"kdt": "kuyî",
"ocu": "tlahuicayî",
"hvn": "sabuyî",
"cjp": "cabecarî",
"xbi": "kombioyî",
"xra": "krahô",
"blc": "bella koola",
"peq": "pomoyiya başûrî",
"bpz": "bilbayî",
"mzp": "movimayî",
"lhu": "lahuyî",
"kge": "komeringî",
"tue": "tuyukayî",
"taa": "tananaya jêrîn",
"vkp": "kreyoliya portugalî ya korlai",
"cmo": "mnongiya navendî",
"mov": "mojaveyî",
"kkz": "kaskayî",
"zro": "zaparoyî",
"mnb": "munayî",
"mwf": "murînpatayî",
"zkg": "goguryeoyî",
"kiw": "kiwaiyiya bakur-rojhilat",
"ebr": "ebrieyî",
"pmt": "tuamotuanî",
"roa-ang": "angevinî",
"ssg": "seimatî",
"hch": "huiçolî",
"bew": "betawî",
"ycn": "yucunayî",
"mbr": "nukak makuyî",
"apq": "a-pucikwar",
"nym": "nyamwezî",
"tbi": "gaamî",
"obi": "obispeñoyî",
"pri": "paicî",
"lu": "luba-katangayî",
"kcg": "tyapî",
"yui": "yurutî",
"kvm": "kendemî",
"djk": "aukanî",
"rir": "ribunî",
"lrg": "laragiayî",
"wnw": "wintuyî",
"woe": "woleayî",
"ljl": "li’oyî",
"kic": "kîkapûyî",
"ndh": "ndalî",
"hdy": "hadiyyayî",
"cbs": "caşinahuayî",
"kys": "baram kayanî",
"nxg": "ngadhayî",
"hix": "hixkaryanayî",
"mpe": "majangî",
"pwm": "molbogî",
"nyn": "nyankoleyî",
"ilb": "îlayî",
"qya": "qiwenyayî",
"wrk": "garawayî",
"def": "dezfuliyî",
"oym": "wayampî",
"rkb": "rikbaktsayî",
"sai-msk": "masakarayî",
"itz": "îtzayî",
"sgc": "kipsigisî",
"ppi": "paipaî",
"tji": "tujiayiya bakurî",
"yak": "yakîmayî",
"gvp": "para gaviãoyî",
"jmc": "maçameyî",
"bfs": "bayiya başûrî",
"ncb": "nicobariya navendî",
"kvb": "kubuyî",
"veo": "venturenyoyî",
"poi": "popolucayiya highland",
"bny": "bintuluyî",
"bhp": "bîmayî",
"rim": "nyaturuyî",
"sog": "sogdî",
"rth": "ratahanî",
"mse": "museyî",
"ncl": "nahwatliya mîçoakanê",
"acp": "akîpayiya rojhilatî",
"pao": "paiuteyiya bakurî",
"iyx": "yaayî",
"zhx-tai": "taişanîzî",
"coe": "koreguajeyî",
"ser": "serranoyî",
"nko": "nkonyayî",
"mgq": "malilayî",
"ppo": "folopayî",
"alc": "kawésqar",
"abg": "abagayî",
"wro": "wororayî",
"kre": "panarayî",
"tuv": "turkanayî",
"dav": "taitayî",
"ppk": "umayî",
"roa-oca": "katalaniya kevn",
"tso-lya": "çolyanî",
"myu": "mundurukú",
"dcc": "deccaniyî",
"lsi": "laşiyî",
"ugo": "gongî",
"nab": "nambikwarayî",
"lil": "lillooetî",
"gqi": "guiqiongî",
"gah": "alekanoyî",
"ncz": "natçezî",
"xom": "komoyî",
"prk": "paraukî",
"bea": "beaverî",
"sky": "sikayanayî",
"pui": "puinaveyî",
"kgg": "kusundayî",
"lml": "ragayî",
"ore": "orejónî",
"vbb": "babariya başûr-rojhilat",
"mbp": "wiwayî",
"bft": "baltî",
"lgg": "lugbarayî",
"mxd": "modangî",
"mir": "isthmus mixeyî",
"enf": "forest enetsî",
"pkp": "pukapukanî",
"kzi": "kelabît bariyoyî",
"wba": "waraoyî",
"xeg": "ǁxegwî",
"cpa": "palantla çinantecî",
"men": "mendeyî",
"btu": "batuyî",
"psy": "piscatawayî",
"xhu": "hurî",
"for": "foreyî",
"deg": "degemayî",
"sai-oto": "otomacoyî",
"nhv": "nahwatliya temascaltepec",
"gme-cgo": "gothîkiya krimî",
"kyi": "kîputî",
"olt": "lîtwanyayiya kevn",
"alp": "aluneyî",
"nbb": "ndoeyî",
"uvl": "loteyî",
"pab": "parecî",
"nbh": "ngamoyî",
"dra-okn": "kannadayiya kevn",
"aak": "ankaveyî",
"wiv": "vîtûyî",
"kuz": "kunzayî",
"atb": "zaiwa",
"aca": "açaguwayî",
"zmr": "maranungkuyî",
"xke": "kerehoyî",
"isk": "îşkaşimiyî",
"dig": "digoyî",
"agn": "agutaynenî",
"bji": "burjî",
"klw": "linduyî",
"bwi": "baniwayî",
"ygr": "yagariayî",
"mez": "menomineeyî",
"kag": "kajamanî",
"mey": "hassaniyayî",
"umo": "umotinayî",
"yii": "yidinyî",
"twy": "tawoyanî",
"ktw": "katoyî",
"ari": "arîkara",
"ysr": "sirenikî",
"adz": "adzerayî",
"spo": "spokanî",
"cgg": "rukîgayî",
"wuv": "wuvulu-auayî",
"etu": "ejaghamî",
"jmd": "yamdenayî",
"mhy": "ma'anyanî",
"tae": "taryanayî",
"kzr": "karangî",
"zhn": "nong zhuangî",
"ess": "yupikiya sîberyaya navendî",
"teg": "teke-tegeyî",
"cqd": "çuanqiandian cluster miaoyî",
"sri": "sirianoyî",
"dag": "dagbaniyî",
"gae": "warekenayî",
"lre": "laurentî",
"tbt": "kitemboyî",
"pgn": "paelignianî",
"onb": "lingaoyî",
"pth": "pataxó hã-ha",
"vmg": "minigirî",
"yah": "yazghulamî",
"kxn": "kanowitî",
"kde": "makondeyî",
"api": "apiaká",
"och": "çîniya kevn",
"pre": "prînsîpenî",
"mgr": "mambwe-lunguyî",
"stp": "tepehuaniya başûr-rojhilat",
"kak": "kayapa kallahanî",
"myg": "mantayî",
"kee": "keresiya rojhilatî",
"mfh": "matalî",
"bkr": "bakumpaî",
"alw": "alaba",
"pml": "sabirî",
"rag": "logûlî",
"mei": "midobî",
"mjy": "mahicanî",
"blm": "belî",
"nhu": "nooneyî",
"kuf": "katuyiya rojavayî",
"cux": "tepeuxila cuicatecî",
"nyy": "nyakyusayî",
"mtf": "murikî (Gîneya nû)",
"mrt": "margî",
"mpj": "martu wangkayî",
"tlh": "klîngonî",
"enh": "tundra enetsî",
"sai-crd": "coroado purî",
"umi": "ukitî",
"tcb": "tanakrosî",
"kpt": "karatayî",
"xvn": "vandalî",
"xsm": "kasemî",
"ltc": "çîniya navîn",
"yuz": "yuracareyî",
"bhw": "biyakî",
"sqt": "soqotrî",
"dny": "deniyî",
"lcp": "lawayiya rojavayî",
"tpx": "akatepekiya me'phaayî",
"ake": "akawaîo",
"jeb": "jeberoyî",
"gup": "gunwingguyî",
"xeb": "eblaiteyî",
"zkt": "xîtanî",
"lex": "luangî",
"mnq": "minriqî",
"njm": "angamî",
"sge": "segayî",
"hoi": "holîkaçukî",
"tee": "huehuetla tepehuayî",
"bil": "bileyî",
"gej": "genî",
"wim": "wik-mungkanî",
"oyd": "oydayî",
"gvj": "guajayî",
"blw": "balangao",
"lgt": "pahîyî",
"sai-cac": "cacanî",
"xok": "xoklengî",
"knv": "taboyî",
"slc": "salibayî (Kolombiya)",
"noz": "nayî",
"bnq": "bantîkî",
"ykm": "yakamulî",
"dym": "yanda dogonî",
"cri": "sãotomenseyî",
"shu": "erebiya çadî",
"tcs": "creoleyiya torres strait",
"gmw-stm": "sathmar swabî",
"rbl": "miraya bikolî",
"mbn": "macaguanî",
"mgu": "mailuyî",
"pib": "yineyî",
"yku": "kuamasî",
"gva": "kaskihayî",
"sai-yur": "yurumanguî",
"clw": "çulymî",
"bln": "bîkolanoyiya başûr katanduanî",
"mxi": "mozarabîkî",
"sob": "sobeyî",
"agd": "agarabiyî",
"bxe": "ongotayî",
"ito": "îtonamayî",
"mmq": "aisî",
"gnd": "zulgo-gemzekî",
"wig": "wik-ngathanayî",
"dto": "tommo soyî",
"suz": "sunwarî",
"rol": "romblomanonî",
"llu": "lawî",
"jaa": "jamamadiyî",
"bns": "bundelî",
"wal": "wolayttayî",
"oui": "oyguriya kevn",
"bjt": "balanta-ganjayî",
"alk": "alakî",
"nhr": "naroyî",
"gmw-vog": "almaniya volga",
"nsn": "nehanî",
"kqe": "kalaganî",
"end": "endeyî",
"jui": "ngadcurî",
"hay": "hayayî",
"svb": "ulau-suainî",
"saz": "sauraştrayî",
"mep": "miriwungî",
"ert": "eritaiyî",
"yen": "yendangî",
"mxb": "mixteciya tezoatlan",
"pmw": "plains miwokî",
"fak": "fangî (Beboid)",
"sai-bet": "betoî",
"arl": "arabela",
"cks": "tayoyî",
"nrn": "nornî",
"kao": "kassonkeyî",
"blb": "bilua",
"wab": "wabî",
"kwd": "kwaioyî",
"abr": "abronî",
"wbi": "wanjî",
"cso": "soçiapam çinantecî",
"kmo": "kwomayî",
"ddw": "dawera-daweloorî",
"zae": "zapotekiya yareni",
"rac": "rasawayî",
"bra": "brajî",
"naz": "nahwatliya koatepek",
"qwm": "kipçakî",
"ayz": "maybratî",
"amp": "alamblakî",
"sit-liz": "lizuyî",
"jum": "jumjumî",
"tub": "tübatulabalî",
"hih": "pamosuyî",
"kbc": "kadiweuyî",
"tun": "tunikayî",
"cod": "cocamayî",
"abt": "ambulasî",
"ame": "yaneşa'",
"xed": "hdî",
"yrb": "yarebayî",
"mbc": "macuşî",
"sai-mal": "malalî",
"khs": "kasuayî",
"soz": "temî",
"mva": "manamî",
"bcu": "bîlîwayî",
"zpo": "zapotekiya amatlan",
"tfr": "teribeyî",
"xgf": "gabrielino-fernandeñoyî",
"aah": "abu' arapeşî",
"adn": "adangî",
"btn": "ratagnonî",
"gby": "gbariyî",
"sxb": "subayî",
"onn": "onobasuluyî",
"aih": "aî-çamî",
"djm": "jamsayî",
"wun": "bunguyî",
"bhv": "bahauyî",
"ahk": "akha",
"tow": "jemezî",
"duw": "dusun wituyî",
"alj": "alanganî",
"sbk": "safwayî",
"kho": "xotanî",
"ndj": "ndambayî",
"mmh": "mehinakuyî",
"ttw": "long watî",
"sai-cat": "catacaoyî",
"kck": "kalangayî",
"yav": "yangbenî",
"heh": "heheyî",
"szw": "sawayî",
"phk": "phakeyî",
"aoa": "angolar",
"xur": "urartûyî",
"txn": "taranganiya rojava",
"piv": "pilenî",
"xta": "mixtekiya alcozauca",
"ute": "uteyî",
"tad": "tauseyî",
"yug": "yugî",
"zum": "kumzarî",
"nkr": "nukuoroyî",
"xaw": "kawaiisuyî",
"mpa": "mpotoyî",
"zng": "mangî",
"gad": "gaddangî",
"drn": "damariya rojava",
"oca": "ocainayî",
"mcg": "mapoyoyî",
"gbm": "garhwaliyî",
"sbh": "sorî-harenganî",
"kew": "kewayî",
"mki": "dhatkî",
"obt": "bretoniya kevn",
"alo": "larike-wakasihu",
"drl": "baagandjiyî",
"tog": "tongayî (Malawî)",
"tdl": "surî",
"met": "matoyî",
"cle": "lealao çinantecî",
"gmq-jmk": "jamtîşî",
"gvf": "golinî",
"tlc": "totonakiya misantla",
"bfg": "busang kayanî",
"kvc": "kovêyî",
"kay": "kamayurayî",
"hei": "heiltsukî",
"urt": "uratî",
"nll": "nihalî",
"bdp": "bendeyî",
"ybe": "yuguriya rojavayî",
"nbr": "numanayî",
"mht": "mandahuacayî",
"wap": "wapişanayî",
"dzg": "dazagayî",
"wrg": "warunguyî",
"tiv": "tivî",
"tox": "tobî",
"kvo": "dobelî",
"ubl": "buhi'non bikolî",
"aja": "ajayî",
"toi": "tongayî (Zambiya)",
"sus": "susuyî",
"nhm": "nahwatliya morelos",
"mcf": "matsesî",
"lew": "ledo kailiyî",
"ckx": "cakayî",
"bxg": "bangalayî",
"nem": "nemî",
"mgo": "meta'",
"eno": "engganoyî",
"crd": "coeur d'aleneyî",
"bdb": "basapî",
"cui": "cuibayî",
"emx": "erromintxelayî",
"bsw": "baysoyî",
"piw": "pimbweyî",
"nim": "nilambayî",
"mzs": "makanîzî",
"njo": "aoyî",
"duk": "uyajitayayî",
"old": "moçî",
"sam": "aramiya samarî",
"mlq": "malinkî",
"awd-kaw": "kawişanayî",
"mro": "mruyî",
"sjm": "mapunî",
"bot": "bongoyî",
"lay": "lama baiyî",
"aif": "agî",
"bks": "masbate sorsogon",
"ibd": "îwaidjayî",
"dob": "dobûyî",
"noa": "woun meuyî",
"too": "totonakiya xicotepec de juarez",
"duv": "duvleyî",
"bqo": "baloyî",
"mna": "mbulayî",
"pbh": "panareyî",
"cav": "cavineñayî",
"scg": "sanggauyî",
"con": "cofanî",
"ubr": "ubirî",
"ata": "pele-ata",
"ngj": "ngieyî",
"ers": "ersuyî",
"tro": "taraoyî",
"yki": "yokeyî",
"tya": "tauyayî",
"byk": "biaoyî",
"tuq": "tedagayî",
"xrn": "arinî",
"mhq": "mandanî",
"ktx": "kaxarariyî",
"beu": "blagarî",
"mtv": "asaro'oyî",
"zca": "zapotekiya coatecas altas",
"abd": "manîdeyî",
"imn": "îmondayî",
"ofo": "ofoyî",
"avv": "avá-kanoeîro",
"ree": "rejang kayanî",
"klr": "khalingî",
"dby": "dibiyasoyî",
"bzd": "bribriyî",
"iwm": "îwamî",
"bvk": "bukatî",
"mgw": "matumbî",
"aus-wul": "wulguruyî",
"mto": "totontepec mixeyî",
"inc-ogu": "gujaratiya kevn",
"kpm": "kohoyî",
"bgz": "banggaî",
"klg": "tagakaulu kalaganî",
"xab": "sambeyî",
"pnz": "panayî (Afrîkaya navendî)",
"snl": "sangilî",
"aoc": "pemon",
"cul": "culinayî",
"nnq": "ngindoyî",
"cji": "çamalalî",
"urh": "urhoboyî",
"hoc": "hoyî",
"yok-nvy": "yokutiya geliyê bakurî",
"nbp": "nnamî",
"qyp": "kuiripî",
"ski": "sikayî",
"whk": "wahau kenyahî",
"fos": "sirayayî",
"paa-nun": "nungonî",
"mtd": "mualangî",
"mrh": "mara çinî",
"xeu": "keoru-ahiayî",
"ass": "îpulo",
"ing": "deg xinagî",
"ajg": "adjayî",
"kjl": "khamî",
"wed": "wedauyî",
"mxz": "maselayiya navendî",
"miw": "akoyeyî",
"lmc": "limilnganî",
"bag": "tukî",
"bva": "bareinî",
"yal": "yalunkayî",
"asa": "pare",
"sai-col": "colanî",
"kdd": "yankunytjatjarayî",
"pne": "penaniya rojavayî",
"hna": "minayî",
"ayp": "erebiya mezopotamyayî ya bakur",
"can": "çambriyî",
"yey": "yeyî",
"szc": "semaq beriyî",
"byd": "benyadu'",
"yok-svy": "yokutiya geliyê başûrî",
"xet": "setayî",
"aro": "araona",
"ntw": "nottowayî",
"szv": "isubuyî",
"crw": "çrauyî",
"kyz": "kayabiyî",
"asl": "asîluluyî",
"poz-abi": "abay-sembwakî",
"tsx": "mubamî",
"ify": "keley-i kallahanî",
"srv": "waray sorsogonî",
"ong": "oloyî",
"mvm": "muyayî",
"lam": "lambayî",
"icl": "zimanê destan ê îzlandê",
"xww": "wemba-wembayî",
"ixl": "îxilî",
"ape": "bukiyîpî",
"rui": "rufijî",
"mhl": "mauwakeyî",
"lgr": "lengoyî",
"nmy": "namuyî",
"ncn": "naunayî",
"pez": "penaniya rojhilatî",
"mfg": "mixiforeyî",
"bei": "bekatî'",
"ydg": "yidghayî",
"cwd": "kriya woods",
"mmn": "mamanwayî",
"nai-kat": "kathlametî",
"cje": "çruyî",
"ark": "arîkapûyî",
"fut": "futuna-anîwayî",
"wad": "wandamenî",
"tew": "tewayî",
"nyo": "nyoroyî",
"bky": "bokyî",
"tjm": "timucuayî",
"ysg": "sonagayî",
"ogu": "ogbronuagumî",
"mnj": "munjî",
"gwd": "gawwadayî",
"fbl": "west albay bikolî",
"uiv": "îyiveyî",
"bkq": "bakairí",
"ibe": "akpesî",
"maa": "mazateciya san jerónimo tecóatl",
"jig": "jinguluyî",
"tvd": "tsuvadî",
"xod": "kokodayî",
"adw": "amondawayî",
"guu": "yanomamöî",
"srh": "sarikolî",
"nam": "ngan'gityemerrî",
"hto": "minica huitotoyî",
"yby": "yaweyuhayî",
"urz": "uru-eu-wau-wau",
"ewo": "ewondoyî",
"cal": "karolînî",
"arr": "arara-karo",
"yup": "yukpayî",
"iai": "îaaiyî",
"wbh": "wandayî",
"dmw": "mudburrayî",
"xel": "keloyî",
"rma": "ramayî",
"zwa": "zayî",
"emi": "mûssaû-emîrakî",
"lai": "lambyayî",
"dim": "dimeyî",
"agu": "agwakatekî",
"zin": "zinzayî",
"kiv": "kimbuyî",
"dbg": "dogul domî",
"bzg": "babuzayî",
"roa-ole": "leoniya kevn",
"bun": "şerbroyî",
"gnn": "gumatjî",
"mhs": "bûrî",
"pln": "palenqueroyî",
"aus-guw": "guwarî",
"tgt": "tagbanwayiya navendî",
"mwv": "mentawaî",
"kke": "kakabeyî",
"pla": "mianî",
"bpr": "koronadal blaanî",
"oty": "tamîliya kevn",
"ywr": "yawuruyî",
"nai-tap": "tapaçultecî",
"sai-sec": "seçurayî",
"pid": "piaroayî",
"mih": "mixteciya çayuco",
"icr": "kreyoliya îngilîzî ya îslanderê",
"hoh": "hobyótî",
"fab": "annoboneseyî",
"ssm": "semnamî",
"ncg": "nisga'ayî",
"mkg": "makî (Çîn)",
"max": "malayiya moluccana bakur",
"zak": "zanakî",
"kxv": "kuviyî",
"xkn": "kayan river kayanî",
"fuy": "fuyugî",
"zpl": "zapotekiya laçixio",
"kwf": "kwara'aeyî",
"aig": "îngilîziya kreyolî ya antîgua û barbud",
"rai": "ramoaainayî",
"otd": "dohoyî",
"bey": "akuwagelî",
"nwr": "nawaruyî",
"tmq": "tumleoyî",
"tgx": "tagişî",
"pbr": "pangwayî",
"loj": "louyî",
"emp": "emberayiya bakurî",
"nho": "takuuyî",
"bco": "kalulî",
"ifb": "batad ifugaoyî",
"jao": "yanyuwayî",
"inc-ohi": "hindiya kevn",
"cup": "cupeñoyî",
"bci": "bawuleyî",
"ngo": "ngonî",
"nun": "anongî",
"yee": "yimasî",
"kxb": "krobuyî",
"hub": "huambisayî",
"nee": "kumakî",
"bec": "iceve-macî",
"smb": "simbarî",
"kuj": "kuriayî",
"ian": "îatmulî",
"cjv": "çuaveyî",
"zne": "zandeyî",
"ssd": "siroî",
"cbn": "nyah kurî",
"lwg": "wangayî",
"bmr": "muinanî",
"kya": "kwayayî",
"xem": "kembayanî",
"acz": "açeronî",
"pav": "wari'",
"egx-dem": "demotîkî",
"bph": "botlixî",
"lev": "lammayî",
"btp": "budibudî",
"bzq": "bulî",
"tdv": "toroyî",
"klq": "rumuyî",
"ojv": "javayiya ontongê",
"bdi": "burunî",
"wya": "wyandotî",
"pbb": "paezî",
"lfn": "lingua franca nova",
"jit": "jitayî",
"amq": "amahaî",
"tcf": "mepaya malînaltepekî",
"ebu": "embuyî",
"rwm": "ambayî",
"pnh": "penrhynî",
"and": "ansus",
"pej": "pomoyiya bakurî",
"kue": "kumanî",
"fip": "fipayî",
"kcn": "nubiyî",
"lol": "mongoyî",
"bwd": "biwaydokayî",
"aia": "arosî",
"zau": "zangskarî",
"pud": "punan aputî",
"ikz": "îkizuyî",
"bgb": "bobongko",
"thk": "tharakayî",
"ymc": "mujiya başûrî",
"mny": "manyawayî",
"xwa": "kwazayî",
"wrs": "warisî",
"nai-tip": "tipaî",
"teu": "sooyî",
"ked": "kereweyî",
"szp": "suaboyî",
"rgr": "resigaroyî",
"bdm": "budumayî",
"nib": "nakameyî",
"lek": "leyponî",
"guh": "guahiboyî",
"bqr": "burusuyî",
"apl": "lipan",
"bnd": "banda",
"tsj": "tşanglayî",
"xme-old": "mediya kevn",
"ppn": "papapanayî",
"mwp": "kala lagaw yayî",
"bfa": "bariyî",
"plh": "paulohî",
"btd": "batakiya dairî",
"ure": "uruyî",
"sty": "tatariya sîberî",
"buy": "mmaniyî",
"xyy": "yorta yortayî",
"xdy": "malayîk dayakî",
"kmj": "kumarbhag pahariayî",
"vun": "vunjoyî",
"cuh": "çukayî",
"lgl": "walayî",
"pna": "punan bah-biauyî",
"now": "nyamboyî",
"gqa": "ga'andayî",
"thm": "thavungî",
"acr": "açiyî",
"cir": "tiriyî",
"mxr": "murîkî (Malezya)",
"swp": "suauyî",
"zor": "zoqueyiya rayón",
"wic": "wiçitayî",
"suw": "sumbwayî",
"bum": "buluyî (Kamerûn)",
"rnw": "rungwayî",
"nak": "nakanayî",
"trs": "trîkuyiya çicahuaxtla",
"lpo": "lipoyî",
"gwe": "gwenoyî",
"cbd": "carijonayî",
"mot": "barî",
"tlv": "taliabuyî",
"xsu": "sanumayî",
"sjl": "mijî",
"pip": "peroyî",
"acx": "erebiya ummanî",
"ybb": "yembayî",
"uln": "unserdeutsch",
"yil": "yindjilandjî",
"ykn": "kua-nsî",
"apu": "apurinã",
"sso": "sissanoyî",
"puc": "punan merapî",
"mwn": "nyamwangayî",
"bpa": "dakakayî",
"mmd": "maonanî",
"bof": "bolonî",
"ati": "attié",
"wji": "warjî",
"lra": "rara bakati'",
"mlu": "toabaîtayî",
"bdd": "bunamayî",
"bfd": "bafutî",
"keu": "akebuyî",
"wod": "wolanî",
"ksb": "şambalayî",
"mff": "nakî",
"azn": "durango nahuatliya rojavayî ya jêrîn",
"asi": "buruwayî",
"ala": "alago",
"tpy": "trumaî",
"zms": "mbesayî",
"kib": "koalibî",
"jun": "juangî",
"guf": "gupapuynguyî",
"liy": "banda-bambariyî",
"sai-pam": "pamiguayî",
"loa": "lolodayî",
"elm": "elemeyî",
"aad": "amalî",
"lcm": "tungagî",
"twh": "tai dónî",
"ppm": "papumayî",
"xmr": "meroytîkî",
"kno": "konoyî (sierra leone)",
"kzz": "kalabrî",
"emw": "emplawasî",
"pnk": "pawnakayî",
"gym": "ngäbereyî",
"mxj": "mijuyî",
"lun": "lundayî",
"kos": "kosraeyî",
"kzx": "kamarianî",
"aan": "anambeyî",
"ukq": "ukwayî",
"tau": "tananaya jorîn",
"anc": "ngas",
"rcf": "kreyola fransî ya réunion",
"knm": "kanamariyî",
"bvy": "baybayanonî",
"jnj": "yemsayî",
"onu": "unuayî",
"bdh": "tara bakayî",
"pij": "pijaoyî",
"mks": "mixteciya silacayoapan",
"juh": "honeyî",
"etx": "etenî",
"sai-chr": "çarruayî",
"aui": "anukî",
"cuc": "usila çinantecî",
"xme-ott": "tatiya kevn",
"njj": "njenî",
"mng": "mnongiya rojhilatî",
"cih": "çinaliyî",
"ard": "arabana",
"val": "vehesî",
"aos": "taykatî",
"peb": "pomoyiya rojhilatî",
"xcb": "kumbrîkî",
"clk": "iduyî",
"aem": "aremî",
"vut": "vuteyî",
"teq": "temeinî",
"siu": "sinagenî",
"xmm": "manado malayî",
"gdj": "kurtjarî",
"xau": "kauwerayî",
"atz": "arta",
"zsr": "zapoteciya rincona başûrî",
"wmt": "walmajarrî",
"ter": "terenoyî",
"sit-sit": "situyî",
"zns": "mangasî",
"kwz": "kwadiyî",
"iki": "îkoyî",
"prq": "aşeninka pereneyî",
"ztq": "zapotekiya quioquitani-quieri",
"wan": "wanî",
"txh": "thrasî",
"ahg": "qimant",
"boe": "mundablî",
"gww": "kwiniyî",
"ior": "înorî",
"ply": "bolyuyî",
"avt": "au",
"zpu": "zapotekiya yalalagî",
"bgv": "warkay-bîpîmî",
"srb": "sorayî",
"huu": "murui huitotoyî",
"zmm": "marimanindjî",
"xme-ker": "kermanî",
"gbv": "gbanuyî",
"awe": "awetí",
"xzh": "zhang-zhungî",
"wow": "wawonî",
"tqo": "toaripî",
"syb": "subaneniya navendî",
"ril": "riangî",
"nqo": "n'koyî",
"laj": "langoyî (Uganda)",
"bbh": "buganî",
"orr": "orumayî",
"gju": "gojriyî",
"ddd": "dongotonoyî",
"yik": "dongşanba laloyî",
"sai-bag": "baguayî",
"tsr": "akeî",
"txe": "totolî",
"nal": "nalikî",
"bez": "kîbenayî",
"jeh": "jehî",
"sem-saf": "safaitîkî",
"mka": "mbreyî",
"nai-jum": "jumaytepequeyî",
"ebg": "ebughuyî",
"cbv": "cacuayî",
"lnl": "bandayiya navendî ya başûr",
"inc-kho": "xolosî",
"tzx": "tabriakî",
"sln": "salînanî",
"ncr": "ncaneyî",
"kqt": "kadazaniya çemê kliasê",
"cbr": "caşibo-cacataiboyî",
"trt": "tunggareyî",
"tyj": "tai doyî",
"kpr": "korafe-yeghayî",
"hmt": "hamtaiyî",
"csi": "coast miwokî",
"bbi": "barombî",
"tvk": "ambrîmiya başûr-rojhilat",
"ynd": "yandruwandhayî",
"sai-sap": "saparayî",
"mik": "mikasukî",
"kid": "koşinî",
"bya": "batakiya palawan",
"awd-ana": "anauyayî",
"yrw": "yarawatayî",
"tnx": "tanemayî",
"lme": "peveyî",
"nbm": "ngbaka ma'boyî",
"myy": "macunayî",
"cao": "çacoboyî",
"anp": "angîka",
"sim": "seimî",
"nha": "nhandayî",
"kqn": "kaondeyî",
"ecr": "eteocretanî",
"crv": "çaurayî",
"bef": "benabenayî",
"aud": "anuta",
"yiy": "yir-yorontî",
"usp": "uspantekoyî",
"sry": "serayî",
"njy": "njyemî",
"mod": "mobilianî",
"mmt": "malalamaî",
"leu": "karayî (Gîneya nû)",
"mbe": "molaleyî",
"txi": "ikpengî",
"snc": "sinaugoroyî",
"noc": "nukî",
"mle": "manambuyî",
"kyr": "kuruayayî",
"ivb": "îbatanî",
"sai-chu": "çuruyayî",
"lsr": "aruopî",
"zaq": "zapotekiya aloapam",
"tef": "teressayî",
"siy": "sivandî",
"nyb": "nyangboyî",
"mpi": "mpadeyî",
"tcq": "kayî",
"kbt": "gabadiyî",
"dka": "dakpayî",
"bff": "bofî",
"ahs": "aşeyî",
"zar": "zapotekiya rincón",
"xct": "tibetiya klasîk",
"afz": "obokuîtayî",
"mnf": "mundanî",
"mpw": "mapidianî",
"gza": "ganzayî",
"acv": "açumawî",
"zmu": "muruwarî",
"zmt": "maringarrî",
"lbc": "lakkiayî",
"gbg": "gbanziriyî",
"des": "desanoyî",
"kud": "awhelawayî",
"agc": "agatuyî",
"tob": "tobayî",
"suc": "subanoniya rojavayî",
"oni": "onînî",
"mne": "nabayî",
"itm": "îtu mbon uzoyî",
"ges": "geser-goromî",
"aza": "azhayî",
"nfl": "aiwooyî",
"tqp": "tomoipî",
"rof": "romboyî",
"ury": "oryayî",
"axg": "mato grosso arára",
"jax": "jambî malayî",
"gkn": "gokanayî",
"tbq-gkh": "gokhyî",
"bkz": "bungku",
"aik": "akeyî",
"tku": "totonaciya nekaksaya jorîn",
"sho": "şangayî",
"crp-rsn": "russenorskî",
"nss": "nalî",
"guo": "guayaberoyî",
"dun": "dusun deyahî",
"bsm": "busamiyî",
"ble": "balanta-kentohe",
"axx": "xaragure",
"sxg": "şixingî",
"apz": "safeyoka",
"awd-pai": "paikonekayî",
"muc": "mbu'",
"kgu": "kobolî",
"dbf": "edopiyî",
"bbp": "bandayiya rojavayî ya navendî",
"loe": "saluanî",
"nux": "mehekî",
"elk": "elkeiyî",
"yaf": "yakayî",
"tiz": "tai hongjinî",
"stu": "samtaoyî",
"nkg": "nekginî",
"koz": "korakî",
"aus-bra": "barranbinyayî",
"tlr": "talîsî",
"spc": "sapeyî",
"sai-pyg": "payaguayî",
"tbq-mil": "milangî",
"klp": "kamasayî",
"crz": "cruzeñoyî",
"boj": "ancamî",
"tan": "tangaleyî",
"svs": "savosavoyî",
"ppq": "peyî",
"ngg": "ngbaka manzayî",
"mpx": "misima-paneatî",
"kcp": "kangayî",
"cbi": "çaçiyî",
"bfc": "bayiya bakurî",
"yec": "yeniçeyî",
"sai-trr": "tarairiú",
"plg": "pilagayî",
"hoe": "horomî",
"yun": "benayî",
"any": "anyî",
"yis": "yisî",
"ush": "uşojoyî",
"tav": "tatuyoyî",
"sve": "serilî",
"gob": "playeroyî",
"nkz": "nkarî",
"moc": "mocovî",
"jko": "kuboyî",
"reg": "karayî (Tanzanya)",
"hul": "hulayî",
"chh": "çinookî",
"yub": "yugambalî",
"tbg": "tayrorayiya bakur",
"mdc": "maleyî",
"kuo": "kumukioyî",
"mvy": "indus kohistanî",
"foi": "foiyî",
"coz": "çoçotecî",
"bim": "bimobayî",
"agf": "argunî",
"kmx": "wabodayî",
"mtl": "montolî",
"kjr": "kuruduyî",
"kcb": "kawaçayî",
"ibn": "îbinoyî",
"scb": "çutî",
"atx": "arutanî",
"mvt": "mpotovoroyî",
"kxa": "kayrîrûyî",
"boq": "bogayayî",
"zpx": "zapotekiya san baltazar loxiça",
"wnc": "wantoatî",
"itd": "tidongiya başûrî",
"xog": "sogayî",
"ann": "obolo",
"unr": "mundarî",
"bzh": "mapos buangî",
"tye": "kyengayî",
"kyq": "kengayî",
"kwa": "dâwî",
"bou": "bondeyî",
"aul": "aulua",
"zty": "zapotekiya yatee",
"wrp": "waropenî",
"mmi": "musarî",
"buq": "bremî",
"auj": "awjîla",
"gtu": "aghu tharrnggalayî",
"zil": "zialoyî",
"tlb": "tobeloyî",
"mty": "nabî",
"lbz": "lardilî",
"gdd": "gêdagêdî",
"mdx": "dizinî",
"bgt": "bugotûyî",
"tpc": "azoyú me'phaayî",
"jmb": "zumbunî",
"tdn": "tondanoyî",
"rng": "rongayî",
"mtm": "matorî",
"djd": "jaminjungî",
"gol": "golayî",
"doz": "dorzeyî",
"nmk": "namakurayî",
"mnt": "maykulanî",
"lbl": "libon bikolî",
"gde": "gudeyî",
"bxs": "busamî",
"xmd": "mbudumî",
"kdq": "koçî",
"cah": "cahuaranoyî",
"bza": "bandiyî",
"aes": "alseayî",
"dsq": "tadaksahakî",
"tmj": "samarokenayî",
"att": "pamplona atta",
"xhd": "hadramî",
"brp": "barapasiyî",
"omw": "tairorayiya başûr",
"nif": "nekî",
"iyo": "mesakayî",
"luq": "lucumiyî",
"kqz": "koranayî",
"coc": "cocopayî",
"amj": "amdang",
"tlk": "talokî",
"spi": "saponî",
"nnf": "ngaingî",
"mlj": "miltuyî",
"luz": "luriya başûrî",
"xas": "kamassî",
"cua": "cuayî",
"afd": "andaî",
"yxl": "yarlî",
"sit-tgm": "tangamî",
"nga": "ngbakayî",
"mrg": "misingî",
"igm": "kanggapeyî",
"gop": "yeretuarî",
"myh": "makahî",
"kpg": "kapîngamarancîyî",
"ero": "horpayî",
"eto": "etonî (Kamerûn)",
"bcq": "bençî",
"srr": "sererî",
"mqz": "malasangayî",
"hui": "huliyî",
"cap": "çipayayî",
"uar": "tayrumayî",
"mox": "molimayî",
"nai-ipa": "ipaî",
"arh": "arhuako",
"wdj": "wadjiginyî",
"tem": "temneyî",
"snn": "sionayî",
"mxk": "monumboyî",
"kru": "kurukhî",
"inc-mas": "asamiya navîn",
"zoo": "zapotekiya asunción mixtepec",
"wme": "wambuleyî",
"pru": "puragî",
"qwt": "kwalhioqua-tlatskanaî",
"gbb": "kaytetyeyî",
"bux": "boghomî",
"atm": "ata",
"abn": "abuayî",
"zab": "zapotekiya san juan guelavia",
"wny": "wanyî",
"sai-qtm": "quitemoyî",
"mmf": "mundatî",
"gdm": "laalî",
"kxi": "keningau murutî",
"dwr": "dawroyî",
"bfw": "bondoyî",
"aux": "aurá",
"ttv": "titanî",
"nrz": "lalayî (Gîneya nû)",
"gbp": "gbaya-bossangoayî",
"awn": "awngî",
"zia": "ziayî",
"way": "wayanayî",
"sut": "subtiabayî",
"rog": "roglayiya bakurî",
"ono": "onondagayî",
"mbj": "nadëbî",
"mls": "masalitî",
"bbj": "ghomalayî'",
"dyi": "djiminiyî",
"bgg": "bugunî",
"ail": "aîmeleyî",
"wir": "wirafedî",
"cla": "ronî",
"ost": "osatuyî",
"kkr": "kir-balarî",
"dgb": "bunogeyî",
"yha": "bahayî",
"mcd": "şaranahuayî",
"rwk": "rwayî",
"ifu": "mayoyao ifugaoyî",
"lig": "ligbiyî",
"jek": "jeri kuoyî",
"ghs": "guhu-samaneyî",
"bdn": "baldemuyî",
"xkf": "xengxayî",
"tsb": "tsamaî",
"sle": "şolagayî",
"nai-spt": "sahaptinî",
"nas": "nasioyî",
"mhd": "mbuguyî",
"kfa": "kodavayî",
"jur": "jurúnayî",
"goa": "guroyî",
"efa": "efaiyî",
"aqn": "altaya bakurî",
"tjs": "tujyayiya başûrî",
"tdy": "tadyawanî",
"los": "loniuyî",
"jka": "kaerayî",
"afo": "eloyî",
"cba-cat": "catio çibçayî",
"bsn": "barasanayî",
"ama": "amanayé",
"sai-san": "sanavironî",
"prc": "paraçî",
"nej": "nekoyî",
"cms": "messapicî",
"kyl": "kalapuyayî",
"coj": "coçimiyî",
"byr": "baruyayî",
"aey": "ameleyî",
"aus-ynk": "yangkaalî",
"yta": "taluyî",
"pay": "peç",
"lse": "lusengoyî",
"cub": "cubeoyî",
"gac": "andamaniya mezin a têkel",
"yry": "yarluyandî",
"tgp": "tangoayî",
"mjt": "sawriya pahariayî",
"phl": "phalurayî",
"ngt": "ngeqî",
"xnr": "kangrî",
"ciy": "çaimayî",
"bca": "bayiya navendî",
"aog": "angoram",
"yrn": "yerongî",
"skz": "sekarî",
"pia": "pima bajoyî",
"mjb": "makaleroyî",
"hot": "hoteyî",
"cbt": "çayahuitayî",
"aor": "aore",
"uve": "uveyiya rojava",
"pbv": "pnarî",
"mkr": "malasî",
"qvy": "kueyuyî",
"nai-chi": "çiquimulillayî",
"bhi": "bhilalî",
"erg": "sieyî",
"plo": "popolucayiya oluta",
"aun": "molmo one",
"kun": "kunamayî",
"laz": "aribwatsayî",
"waj": "waffayî",
"xsi": "sioyî",
"ibr": "îbuoroyî",
"zaf": "zapotekiya ayoquesco",
"lbq": "wamparî",
"keg": "teseyî",
"nyt": "nyawaygî",
"akc": "mpur",
"mnx": "manikionî",
"kmq": "kwamayî",
"khz": "keaparayî",
"ilk": "îlongotî",
"gaj": "gadsupî",
"aqp": "atakapa",
"kgr": "abunî",
"zpf": "zapotekiya san pedro quiatoni",
"sok": "sokoroyî",
"odu": "odualî",
"muh": "mündü",
"lbb": "labelî",
"knl": "keninjalî",
"bna": "boneratî",
"auy": "auyana",
"ade": "adeleyî",
"zpw": "zapotekiya zaniza",
"opm": "oksapminî",
"mug": "musguyî",
"hob": "mariyiya awistronezî",
"ywl": "laluyiya rojavayî",
"ksx": "kêdangî",
"gbq": "gbaya-bozoumî",
"xbm": "bretoniya navîn",
"kmn": "awtuwî",
"ziw": "zigulayî",
"wbm": "wayî",
"tgo": "sudestî",
"opo": "opaoyî",
"lan": "laruyî",
"xuu": "khweyî",
"gid": "gidarî",
"bvj": "baanî",
"zzj": "zuojiang zhuangî",
"wpc": "wirö",
"rnn": "roonî",
"don": "dourayî",
"bhj": "bahingî",
"lib": "likumî",
"akj": "jeru",
"awd-gnu": "guinauyî",
"dux": "duunî",
"bqp": "busayî",
"nsk": "naskapî",
"mqb": "mbukoyî",
"kwl": "kofyarî",
"nai-jtp": "jutiapayî",
"grd": "guruntumî",
"llp": "efateyiya bakur",
"agy": "altaya başûrî",
"yar": "yabaranayî",
"khc": "tukang besiya bakur",
"sbl": "sambaliya botolan",
"crc": "lonwolwolî",
"xkg": "kagoroyî",
"hdn": "haidayiya bakurî",
"sai-ctq": "catuquinaruyî",
"bbb": "barayî",
"amr": "amarakaerî",
"yky": "yakomayî",
"bsx": "yangkamî",
"tmz": "tamanakuyî",
"zsa": "sarasirayî",
"nig": "ngalakanî",
"khl": "lusiyî",
"xow": "kowakî",
"hem": "hembayî",
"pga": "erebiya jubayê",
"bhm": "batharî",
"bzb": "andioyî",
"yko": "yasayî",
"sax": "sayî",
"ypg": "pholayî",
"nnh": "ngiemboonî",
"mpd": "maçinereyî",
"vic": "kreyoliya giravên virginê",
"kdp": "kaningdon-nindemî",
"sit-cha": "çairelî",
"yes": "yeskwayî",
"uur": "urayî (Vanuatu)",
"mhu": "taraonî",
"slu": "selaruyî",
"xtd": "mixtekiya diuxi-tilantongo",
"utr": "etuloyî",
"bnv": "benerafî",
"ydk": "yoidikî",
"trr": "tauşîroyî",
"srk": "serudung murutî",
"xpo": "poçutekî",
"nkx": "nkorooyî",
"kes": "kugboyî",
"kgm": "karipúnayî",
"cax": "çiquitanoyî",
"big": "biangaî",
"vil": "vilelayî",
"dtu": "tebul ure dogonî",
"tty": "sikaritaî",
"rmg": "norweciya traveller",
"mlw": "molokoyî",
"biq": "bipî",
"ylu": "aribwaungî",
"wga": "wagayayî",
"tnz": "ten'ednî",
"qum": "sipakapenseyî",
"pue": "puelçeyî",
"nop": "numanggangî",
"mzq": "mori atasî",
"awd-kus": "kustenauyî",
"idi": "îdî",
"kzf": "da'a kailiyî",
"bib": "bîssayî",
"zad": "zapotekiya cajonos",
"waz": "wampurî",
"tll": "tetelayî",
"pur": "puruborayî",
"nih": "nyihayî",
"tnk": "kwamerayî",
"ilv": "îlueyî",
"dmn-dam": "damayî (Sierra leone)",
"cba-ata": "atanquesî",
"abu": "abureyî",
"zpt": "zapotekiya san vicente coatlan",
"aaw": "solongyî",
"opy": "ofayeyî",
"dtb": "labuk-kinabatangan kadazanî",
"ikt": "înuvialuktunî",
"auu": "auye",
"adi": "adî",
"wsk": "waskiayî",
"tix": "tiwayiya başûrî",
"atu": "reel",
"mui": "musî",
"iso": "îsokoyî",
"gya": "gbayayiya bakur-rojava",
"auw": "awyî",
"zik": "zimakanî",
"sai-way": "wayumarayî",
"sue": "suenayî",
"sai-opo": "opónî",
"srf": "nafî",
"qxs": "kiangiya başûrî",
"ggt": "gituayî",
"bcd": "babariya bakur",
"xam": "ǀxamî",
"wiu": "wiruyî",
"rro": "roroyî",
"osi": "osingî",
"mvb": "mattolî",
"mjk": "matukarî",
"lln": "leleyî (Çad)",
"kiy": "kirikiriyî",
"sit-jap": "japhugî",
"tds": "doutaî",
"blf": "buol",
"akt": "akolet",
"apb": "sa'a",
"awd-mpr": "maypureyî",
"jee": "jerungî",
"saj": "sahuyî",
"pkn": "pakanhayî",
"kkx": "kohinî",
"nai-cal": "calusayî",
"bgf": "banganduyî",
"ybm": "yabenî",
"bhq": "tukang besî başûr",
"mxe": "mele-filayî",
"lom": "loomayî",
"hka": "kaheyî",
"emn": "emanî",
"boi": "barbarenyoyî",
"aie": "amarayî",
"anh": "nendî",
"mej": "meyahî",
"kcl": "kalayî",
"hro": "haroiyî",
"enc": "enî",
"amy": "amî",
"yde": "yangum deyî",
"taj": "tamangiya rojhilatî",
"sar": "saravekayî",
"szb": "ngalumî",
"inm": "minaeanî",
"kwh": "kowyayî",
"hed": "herdeyî",
"cuy": "cuitlatecî",
"yvt": "yaviteroyî",
"sgw": "sebat bet gurageyî",
"nph": "phomî",
"mjs": "mişipî",
"mda": "madayî (Nîjerya)",
"wat": "kaninuwayî",
"blo": "anîî",
"yss": "yessan-mayoyî",
"ona": "selk'namî",
"pny": "pinyinî",
"noe": "nimadî",
"crm": "kriya mûsî",
"hhy": "hoyahoyayî",
"bzz": "evantî",
"inc-apa": "apabhramsayî",
"uuu": "uyî",
"seu": "serui-lautî",
"pbi": "podokoyî",
"mhc": "moçoyî",
"kzw": "kaririyî",
"faa": "fasuyî",
"sai-chi": "çirinoyî",
"yab": "yuhupî",
"vit": "vitî",
"myl": "momayî",
"crb": "island caribî",
"frq": "forakî",
"cog": "çongî",
"brk": "birgidî",
"waq": "wagemanî",
"nuy": "nunggubuyuyî",
"mdb": "morigî",
"idu": "îdomayî",
"fun": "fulniôî",
"dao": "daai çinî",
"wnb": "wanambreyî",
"skv": "skouyî",
"sai-pur": "purukotó",
"nyi": "nyimangî",
"kwn": "kwangaliyî",
"kec": "keigayî",
"gbu": "gagaduyî",
"dnj": "danî",
"bus": "bokobaruyî",
"ado": "abuyî",
"zpn": "zapotekiya santa ines yatzeçi",
"aml": "war-jaîntî",
"mij": "mungbamî",
"lbu": "labuyî",
"ken": "kenyangî",
"ipo": "îpikoyî",
"zam": "zapotekoyiya mahuatlana navendî",
"wac": "wasko-wişramî",
"sww": "sowayî",
"msu": "musomî",
"rkm": "markayî",
"slp": "lamaholotî",
"gso": "gbayayiya başûr-rojava",
"ahb": "axamb",
"kit": "agobî",
"bfi": "zimanê destan ê brîtanî",
"taz": "toçoyî",
"swi": "suî",
"prm": "kibirî",
"gis": "gizigayiya bakur",
"doe": "doeyî",
"abj": "aka-beayî",
"hnh": "ǁaniyî",
"roo": "rotokasî",
"nlv": "nahwatliya orizaba",
"npl": "nahwatliya pueblaya başûr-rojhilatî",
"llc": "leleyî (Gîne)",
"fkk": "kirya-konzelî",
"mde": "bura mabangî",
"bdl": "bajwayiya endonezyayî",
"bss": "akooseyî",
"scw": "şayî",
"lsm": "saamiayî",
"pqa": "pa'ayî",
"nmq": "nambyayî",
"mbq": "maysînî",
"jib": "jibuyî",
"bhs": "buwalî",
"sjo": "xibeyî",
"abw": "pal",
"nxa": "naueteyî",
"mck": "mbundayî",
"jmx": "juxtlahuaca mixteciya rojavayî",
"clu": "caluyanunî",
"ybo": "yabongî",
"tuo": "tukanoyî",
"nml": "ndemlî",
"mfx": "meloyî",
"lop": "lopayî",
"kzv": "komyandaretî",
"kct": "kaianî",
"has": "haislayî",
"mmw": "emayî",
"cyb": "cayubabayî",
"bcj": "bardî",
"awd-ama": "amarizanayî",
"ylr": "yalarnngayî",
"ago": "taînaeyî",
"prw": "parawenî",
"enw": "enwangî",
"kog": "coguiyî",
"bst": "basketoyî",
"ymo": "yangum monî",
"uda": "udayî",
"tma": "tamayî (Çad)",
"sre": "sarayî",
"nbv": "ngamamboyî",
"mcv": "minanibaî",
"koh": "koyoyî",
"zbt": "batuî",
"anb": "andoa",
"yaw": "yawalapitî",
"tnc": "tanimuca-retuarã",
"sbx": "seberuangî",
"yhl": "hlepho phowayî",
"bzv": "bebeyî",
"anf": "anîmere",
"yet": "yetfayî",
"pir": "piratapuyoyî",
"nmw": "nimoayî",
"nhb": "bengî",
"tbo": "tawalayî",
"szd": "seruyî",
"poy": "pogoloyî",
"bve": "malayiya berau",
"ctm": "çitimaçayî",
"aaf": "aranadanî",
"mkh-mvi": "viyetnamiya navîn",
"mnd": "mondeyî",
"mup": "malvî",
"kse": "kuniyî",
"btf": "birgîtî",
"aac": "ariyî",
"tex": "tennetî",
"kbb": "kaxuyanayî",
"ula": "fungwayî",
"dsh": "daasanaçî",
"zps": "zapotekiya coatlan",
"wnp": "wanapî",
"scs": "slaveyiya bakur",
"lid": "nyindrouyî",
"kvi": "kwangî",
"idb": "îndo-portegîzî",
"bdt": "bokotoyî",
"ate": "atemble",
"zpi": "zapotekiya santa maria quiegolani",
"tvy": "timor pidginî",
"sol": "solosî",
"rjs": "rajbanşî",
"ogc": "ogbahî",
"mth": "mungguî",
"dmc": "gavakî",
"zaj": "zaramoyî",
"sss": "soyî",
"nai-okw": "okwanuçuyî",
"hia": "lamangî",
"gyg": "gbayiyî",
"duy": "dicamay agtayî",
"sgt": "brokpakeyî",
"yix": "axi yî",
"tcx": "todayî",
"sui": "sukî",
"phi-nag": "nagtipunan agtayî",
"ixc": "îxcatecî",
"giz": "gizigayiya başûr",
"baa": "babatanayî",
"akm": "aka-bo",
"wiy": "wiyotî",
"tnt": "tontemboanî",
"uta": "otankî",
"nhc": "nahwatliya tabasco",
"mph": "maungî",
"led": "lenduyî",
"kiz": "kisiyî",
"dva": "duauyî",
"bvr": "burarrayî",
"yig": "wusa nasuyî",
"neb": "tourayî",
"shv": "şehrî",
"nnm": "namiayî",
"nai-wji": "jicaqueyiya rojavayî",
"gve": "duwetî",
"yln": "buyangiya langnî",
"blq": "baluan-pam",
"jiv": "şuarî",
"quv": "sakapultekoyî",
"ynk": "naukanskî",
"mgy": "mbungayî",
"kpx": "koiariyiya çiyayî",
"ktz": "juǀ'hoanî",
"ofu": "efutopî",
"wes": "cameroon pidginî",
"dba": "bangi meyî",
"aab": "alumu-tesuyî",
"bkj": "pande",
"ndg": "ndengerekoyî",
"bbo": "konabereyî",
"zka": "kaimbulawayî",
"hgm": "haiǁomî",
"alv-ama": "amasî",
"ttm": "tutçoneyiya bakurî",
"nen": "nengoneyî",
"zmf": "mfinuyî",
"ruf": "luguruyî",
"kfq": "korkuyî",
"tmd": "haruaî",
"eot": "eotileyî",
"sem-amm": "ammoniteyî",
"tdk": "tambasî",
"sba": "ngambayî",
"lue": "luvaleyî",
"khq": "koyra çiiniyî",
"doc": "kamiya bakurî",
"sai-cul": "cullî",
"ano": "andoque",
"yww": "yawarawargayî",
"uji": "tanjijilî",
"ypz": "phuzayî",
"nsq": "miwokiya sierraya bakurî",
"sgb": "mag-ançî aytayî",
"ish": "esanî",
"aoi": "anîndîlyakwa",
"uri": "urimî",
"yer": "tarokî",
"shb": "ninamî",
"kpj": "karajayî",
"hoz": "hozoyî",
"twm": "tawang monpayî",
"mqo": "modoleyî",
"meo": "malayiya kedah",
"bbv": "karnaî",
"agl": "fembeyî",
"ctg": "çittagonianî",
"tkq": "teeyî",
"wmd": "mamaindeyî",
"kjn": "kunjenî",
"kmg": "kâteyî",
"bqq": "biritaiyî",
"aai": "arîfama-mînîafîayî",
"swj": "sirayî",
"puw": "puluwatî",
"rin": "nunguyî",
"bdo": "moromî",
"khj": "kuturmiyî",
"fvr": "furî",
"dbq": "dabayî",
"blr": "blang",
"mfi": "wandalayî",
"xsl": "slaveyiya başûr",
"pww": "pwoyiya bakurî",
"knp": "kwanjayî",
"gbi": "galelayî",
"bqc": "bokoyî",
"zpr": "zapotekiya santiago xanica",
"sai-tim": "timoteyî",
"rki": "rakhineyî",
"ogb": "ogbiayî",
"mpv": "mungkipî",
"ker": "kerayî",
"iqu": "îquitoyî",
"gou": "gavarî",
"bvf": "boorî",
"tmb": "avavayî",
"was": "waşoyî",
"tkp": "tîkopyayî",
"reb": "rembongî",
"ogo": "khanayî",
"diu": "gcirikuyî",
"orw": "oro wînî",
"nlx": "nahalî",
"vme": "maselayiya rojhilat",
"ltn": "latundê",
"bvx": "baboleyî",
"aci": "aka-karî",
"rug": "rovyanayî",
"kqs": "kissiya bakurî",
"dmu": "dubuyî",
"bwr": "burayî",
"bkc": "baka",
"akf": "akpayî",
"wut": "wutungî",
"she": "şekoyî",
"pma": "paamayî",
"mgi": "jilî",
"gvl": "gulayî",
"dyy": "dyaabugayî",
"aiy": "aliyî",
"xin": "xincayî",
"skf": "sakirabiayî",
"zmp": "mbuunî",
"bex": "jûr modoyî",
"mea": "menkayî",
"kfc": "konda-dorayî",
"kbw": "kaîepî",
"cto": "embera-catioyî",
"cam": "cemuhî",
"bae": "baré",
"tce": "tutçoneyiya başûrî",
"nfu": "mfumteyî",
"lcq": "luhuyî",
"oco": "korniya kevn",
"ktg": "kalkatunguyî",
"sja": "epenayî",
"col": "columbia-wenatçiyî",
"alx": "amol",
"wca": "yanomamî",
"xxt": "tamborayî",
"auk": "heyo",
"cuo": "cumanagotoyî",
"sai-and": "andoqueroyî",
"yva": "yawayî",
"due": "umiray dumaget agtayî",
"kml": "tanudan kalingayî",
"ptr": "piamatsinayî",
"blx": "mag-îndî ayta",
"gcl": "kreyoliya îngilîzî ya grenadî",
"ese": "ese ejjayî",
"uvh": "urî",
"nsm": "semayî",
"nin": "ninzoyî",
"hra": "hrangkholî",
"nai-taw": "tawasayî",
"sbi": "setî",
"mvq": "moereyî",
"mqg": "malayiya kota bangun kutai",
"uwa": "kuku-uwanhî",
"iow": "çiwereyî",
"bid": "bidiyoyî",
"yuk": "yukî",
"teh": "tehuelçeyî",
"mkc": "siliputî",
"zle-ono": "novgorodiya kevn",
"wmh": "waima'ayî",
"tio": "teopî",
"yae": "pumeyî",
"mqn": "moroneneyî",
"zma": "mandayî (Awistralya)",
"kdc": "kutuyî",
"kyt": "kayagarî",
"mps": "dadibî",
"byn": "blinî",
"cns": "asmatiya navendî",
"utp": "abayî",
"zpc": "zapotekiya çoapan",
"den": "slaveyî",
"pwo": "pwoyiya rojavayî",
"mub": "mubî",
"mev": "manoyî",
"bij": "kwankayî",
"kyo": "kelonî",
"inb": "îngayî",
"aph": "athpare",
"zas": "zapotekiya santo domingo albarradas",
"ral": "ralteyî",
"irn": "îrantxeyî",
"dge": "degenanî",
"boa": "borayî",
"adj": "adyukruyî",
"zal": "zauzouyî",
"wax": "watamî",
"tca": "tîkunayî",
"agv": "remontado agta",
"omg": "omaguayî",
"xru": "marriammuyî",
"khn": "khandeşiyî",
"gji": "gejiyî",
"ayd": "ayabadhu",
"zmb": "zimbayî",
"toj": "tojolabalî",
"syk": "sukurî",
"bnf": "masiwang",
"glw": "glavdayî",
"bav": "babungoyî",
"akx": "aka-kede",
"ots": "otomiya estado de meksîka",
"mjj": "mawakî",
"leh": "lenjeyî",
"kss": "kissiyiya başûrî",
"kbz": "duhwayî",
"sth": "şeltayî",
"myf": "bambassî",
"ald": "alladînî",
"ego": "eggonî",
"cbu": "candoşi-şaprayî",
"tmc": "tumakî",
"pgs": "pangsengî",
"ndz": "ndogoyî",
"knd": "kondayî",
"haj": "hajongî",
"cmi": "embera-çamiyî",
"mlp": "bargamî",
"yat": "yambetayî",
"bsg": "başkardiyî",
"pek": "pençalî",
"lwo": "luwoyî",
"kpk": "kpanî",
"kdx": "kamî",
"cug": "cungî",
"anx": "andra-hus",
"sey": "secoyayî",
"pio": "piapocoyî",
"jgo": "ngombayî",
"miy": "mixteciya ayutla",
"gyn": "kreyoliya îngilîzî ya guyanî",
"hoa": "hoavayî",
"amx": "anmatyerre",
"tdm": "tarumayî",
"sza": "semelayî",
"pit": "pitta-pittayî",
"nsz": "nisenanî",
"mis-mmd": "mîmiya decorseyî",
"mky": "makianiya rojhilat",
"kdj": "karamojongî",
"hre": "hreyî",
"bie": "bepourî",
"ena": "apaliyî",
"vgr": "vaghrî",
"sze": "sezeyî",
"not": "nomatsiguengayî",
"mif": "mofu-gudurî",
"mfp": "malayiya makassar",
"kul": "kulereyî",
"huo": "huyî",
"biu": "biete",
"twe": "teiwayî",
"stv": "silt'eyî",
"mqj": "mamasayî",
"kug": "kupayî",
"chw": "çuaboyî",
"zkr": "zakhringî",
"waw": "waiwaî",
"ntp": "tepehuaniya bakurî",
"pmi": "pumiya bakurî",
"hux": "nüpode huitotoyî",
"mor": "moroyî",
"mha": "mandayî (Hindistan)",
"thd": "kuuk thaayorreyî",
"dgz": "dagayî",
"mae": "bo-rukulî",
"sjg": "assangorî",
"zpg": "zapotekiya guevea de humboldt",
"tcz": "thado çinî",
"nyv": "nyulnyulî",
"aso": "dano",
"bld": "bolango",
"snk": "soninkeyî",
"sai-mar": "maratinoyî",
"laf": "lafofayî",
"krk": "kerekî",
"gyl": "gayilî",
"ksr": "borongî",
"avi": "avîkam",
"wah": "watubelayî",
"tpl": "tlacoapa me'phaayî",
"mfr": "marrithiyelî",
"khr": "khariayî",
"giq": "gelaoyiya hêşîn",
"aye": "ayere",
"owl": "weylsiya kevn",
"zsu": "sukurumî",
"nhk": "nahwatliya cosoleacaque",
"law": "laujeyî",
"izr": "îzereyî",
"goi": "gobasiyî",
"wog": "wogamusinî",
"ttu": "torauyî",
"dts": "toro so dogonî",
"mcw": "mawayî",
"azc-ktn": "kitanemukî",
"ddj": "jaruyî",
"dma": "dumayî",
"bkh": "bakoko",
"aqz": "akuntsu",
"pdo": "padoeyî",
"nnb": "nandeyî",
"dzl": "dzalayî",
"bax": "bamumî",
"xiy": "xipayayî",
"tct": "t'enî",
"lht": "lo-togayî",
"kkk": "kokotayî",
"lot": "lotukoyî",
"tdc": "embera-tadó",
"bjc": "barijî",
"aew": "ambakîç",
"tkm": "takelmayî",
"sbp": "sanguyî (Tanzanya)",
"sai-ptg": "patagónî",
"lgk": "neververî",
"ybj": "haşayî",
"amg": "amurdag",
"yns": "yansî",
"udu": "udukî",
"mwm": "sarî",
"ums": "pendauyî",
"wii": "minidienî",
"pbn": "kpasamî",
"cuq": "cunî",
"bbd": "bauyî",
"aty": "aneityum",
"ngc": "ngombeyî (Kongo)",
"kxo": "kanoeyî",
"ags": "esîmbî",
"bxb": "belanda borî",
"urn": "uruangnirinî",
"mis-mmn": "mîmiya naçtigalî",
"hru": "hrusoyî",
"apt": "apatanî",
"yon": "yongkomî",
"vag": "vaglayî",
"vkl": "kulisusuyî",
"kxh": "karoyî",
"jab": "hyamî",
"smg": "simbalî",
"asj": "nsarî",
"knk": "kurankoyî",
"kaf": "katsoyî",
"yml": "îamaleleyî",
"brt": "bitareyî",
"bao": "waîmaha",
"tla": "tepehuaniya başûr-rojavayî",
"pmj": "pumiya başûrî",
"nuf": "nusuyî",
"mrl": "mortlockeseyî",
"man": "mandingoyî",
"ign": "îgnacianoyî",
"bzn": "boanoyî (Maluku)",
"xss": "assanî",
"wne": "wanecî",
"thf": "thangmî",
"kjb": "q'anjob'alî",
"sai-muc": "mucuçî",
"msw": "mansoankayî",
"kem": "kemakî",
"bnp": "bolayî",
"aqm": "atohwaim",
"zat": "zapotekiya tabaa",
"sov": "sonsorolî",
"lhn": "lahananî",
"kjq": "keresiya rojavayî",
"dei": "demisayî",
"kwi": "awa-cuaiquerî",
"aez": "aekayî",
"zay": "zayse-zergullayî",
"una": "watutiya bakur",
"ctl": "tlacoatzintepec çinantecî",
"squ": "squamîşî",
"omb": "ombayî",
"sur": "mwaghavulî",
"umc": "marrusînî",
"lbn": "lametî",
"phi-din": "dinapigue agtayî",
"buw": "bubiyî",
"ahn": "ahàn",
"zrn": "zirenkelî",
"srg": "sulodî",
"ora": "orohayî",
"mtn": "matagalpayî",
"kco": "kinalaknayî",
"hmd": "a-hmaoyî",
"woc": "wogeoyî",
"tqr": "toronayî",
"rbb": "rumaî palaungî",
"ott": "otomiya temoaya",
"nhi": "nahwatliya zacatlan-ahuacatlan-tepetzintla",
"mgk": "mawesî",
"dus": "dumiyî",
"bzu": "burmesoyî",
"bbu": "bakulungî",
"trn": "trinitarioyî",
"sdp": "şerdukpenî",
"sbv": "sabineyî",
"sai-jrj": "jirajirayî",
"gvs": "gumawanayî",
"dnn": "dzuunî",
"tte": "bwanabwanayî",
"bcf": "bamuyî",
"xir": "xiriânayî",
"two": "tswapongî",
"sai-tab": "tabancaleyî",
"lgq": "logbayî",
"klm": "kolomî",
"ngs": "gvokoyî",
"eka": "ekajukî",
"dbw": "bankan tey dogonî",
"aly": "alyawarr",
"yad": "yaguayî",
"ndp": "ndoyî",
"mcr": "menyayî",
"xon": "konkombayî",
"kka": "kakandayî",
"hao": "haköyî",
"ckl": "cibakî",
"jbk": "barikewayî",
"abc": "ambala ayta",
"yaa": "yaminahuayî",
"sny": "saniyo-hiyeweyî",
"mmc": "miçoacan mazahuayî",
"xkq": "koronî",
"had": "hatamî",
"sai-cop": "copallenî",
"swy": "saruayî",
"nid": "ngandî",
"mhi": "ma'dî",
"cpo": "kpeeyî",
"anz": "anem",
"unz": "unde kailî",
"sai-tpr": "taparitayî",
"sed": "sedangî",
"fpe": "piçinglisî",
"mxy": "mixteciya noçixtlana başûr-rojhilatî",
"bfz": "mahasu paharî",
"sai-esm": "esmeraldayî",
"urv": "uruavayî",
"seh": "senayî",
"spx": "pîkeneyiya başûr",
"cag": "nivacleyî",
"zkk": "karankawayî",
"bkl": "berik",
"yut": "yopnoyî",
"vai": "vaî",
"slw": "sialumî",
"noj": "nonuyayî",
"caf": "karrieriya başûrî",
"fag": "finonganî",
"ibh": "bihî",
"awt": "araweté",
"tyx": "teke-tyeeyî",
"mog": "mongondowî",
"knt": "katukinayî",
"bwm": "biwatî",
"apr": "arop-lokep",
"atr": "waîmîrî-atroarî",
"mtt": "motayî",
"pym": "fyamî",
"dlk": "dahalikî",
"bzl": "boanoyî (sulawesî)",
"abp": "abenlen ayta",
"zaw": "zapotekiya mitla",
"svm": "slavomolisanoyî",
"zpz": "zapotekiya texmelucan",
"tit": "tiniguayî",
"rax": "rangî",
"asx": "muratayak",
"mvr": "marauyî",
"lha": "lahayî (Viyetnam)",
"irh": "îrarûtûyî",
"sai-gay": "gayónî",
"awi": "aekyom",
"mcy": "watutiya başûr",
"tpt": "tlaçiçilco tepehuayî",
"sto": "stoneyî",
"rea": "rerauyî",
"aom": "ömie",
"tlx": "khehekî",
"mrx": "dineorî",
"oro": "orokoloyî",
"acl": "akar-baleyî",
"wbw": "woî",
"otn": "otomiya tenango",
"gia": "kitjayî",
"ats": "gros ventre",
"tri": "trió",
"zmz": "mbandjayî",
"yzg": "e'ma buyangî",
"bwa": "bwatûyî",
"bcw": "banayî",
"ywt": "xişanba laloyî",
"tsc": "tswayî",
"gmw-tsx": "saksoniya transylvanyayê",
"fap": "palorî",
"nwa": "nawathinehenayî",
"mue": "media lenguayî",
"kcx": "kaçama-ganjuleyî",
"dah": "gwahatikeyî",
"ekg": "ekariyî",
"cbc": "carapanayî",
"bxw": "bankayî",
"ygw": "yagwoiayî",
"tbr": "tumtumî",
"tgw": "tagwana senoufoyî",
"pnr": "panimî",
"njt": "ndyuka-trio pidginî",
"vnk": "lovonoyî",
"kma": "konniyî",
"hal": "halangî",
"awc": "cicipu",
"bch": "bariyayî",
"gfk": "patpatarî",
"ule": "luleyî",
"vkm": "kamakanî",
"xht": "hattîkî",
"btz": "batakiya alas-kluet",
"dti": "ana tinga dogonî",
"yre": "yaoureyî",
"udj": "ujirî",
"tpu": "tampuanî",
"ung": "ngarinyinî",
"xjb": "minjungbalî",
"slz": "ma'yayî",
"kpe": "kpelleyî",
"xla": "kamulayî",
"kpc": "curripacoyî",
"bvz": "bawziyî",
"ets": "yekheeyî",
"bbn": "uneapayî",
"tvw": "sedoayî",
"crp-spp": "pidginiya samoan plantation",
"ngq": "ngoremeyî",
"mil": "mixteciya peñoles",
"sai-chn": "çanayî",
"kvu": "yinbaw karenî",
"txx": "tatanayî",
"lva": "maku'ayî",
"gel": "fakkanciyî",
"bom": "beromî",
"cvn": "valle nacional çinantecî",
"sya": "siangî",
"bql": "bilakurayî",
"arv": "arbore",
"sai-yup": "yupuayî",
"vnp": "vunapuyî",
"sbb": "simboyî",
"gmw-pro": "proto-cermeniya rojava",
"nbn": "kurî",
"wbv": "wajarrî",
"mwh": "moûk-arîakî",
"bwp": "mandobo bawahî",
"fie": "fyerî",
"dal": "dahaloyî",
"aba": "abeyî",
"zpm": "zapotekiya mixtepec",
"wky": "wangkayutyuruyî",
"laq": "qabiaoyî",
"nja": "nzanyî",
"dmo": "kemezungî",
"dro": "daro-matu melanauyî",
"ato": "atong (Kamerun)",
"yif": "açeyî",
"zts": "zapotekiya tilquiapan",
"lah": "lahndayî",
"iry": "îrayayî",
"roa-bbn": "bourbonnais-berriçonî",
"aal": "afadeyî",
"czn": "zenzontepekiya çatîno",
"mpl": "watutiya navîn",
"lmg": "lamogayî",
"nlg": "gelayî",
"ayo": "ayoreoyî",
"zoh": "zoqueyiya çimalapa",
"mtp": "wiçi lhamtes noctenî",
"suv": "sulungî",
"xmt": "matbatî",
"zga": "kingayî",
"jbt": "jabutiyî",
"gof": "gofayî",
"rmt": "domarî",
"bwu": "bulî (Gana)",
"jod": "wojenakayî",
"yyu": "torricelli yauyî",
"sbf": "şaboyî",
"yan": "mayangnayî",
"kii": "kitsaiyî",
"trc": "trîkuyiya copala",
"shx": "şeyî",
"pgu": "paguyî",
"gwa": "mbatoyî",
"dyo": "jola-fonyiyî",
"yas": "gunuyî",
"igb": "ebirayî",
"plz": "paluanî",
"alv-gwa": "gwarayî",
"eke": "ekitî",
"cfd": "carayî",
"abb": "bankonî",
"amc": "amahuaca",
"rhp": "yahangî",
"lmb": "mereiyî",
"koa": "konomalayî",
"hlb": "halbiyî",
"eme": "emerillonî",
"nai-cig": "ciguayoyî",
"ael": "ambeleyî",
"jay": "yan-nhanguyî",
"tak": "talayî",
"mmy": "migaamayî",
"lmd": "lumunî",
"kfp": "korwayî",
"woi": "kamangî",
"akg": "anakalunguyî",
"nai-per": "pericú",
"nrl": "ngarlumayî",
"mew": "maakayî",
"hig": "kamweyî",
"hnd": "hindkoyiya başûrî",
"cuv": "cuvokî",
"ylg": "yeloguyî",
"une": "unemeyî",
"tpj": "tapieteyî",
"sos": "seenkuyî",
"nsh": "ngoşieyî",
"mit": "mixteciya pueblaya başûr",
"mxx": "mahouyî",
"bzk": "kreyoliya nîkaraguayî",
"esq": "esselenî",
"nbc": "çangî",
"anu": "anuak",
"snu": "senggî",
"mpu": "makurapî",
"hbb": "hubayî",
"cbg": "çimilayî",
"wti": "bertayî",
"app": "apma",
"spg": "sianî",
"snf": "noonî",
"mkp": "moikodî",
"viv": "idunayî",
"gem-pro": "proto-cermenî",
"omx": "moniya kevn",
"maw": "mamprulî",
"are": "arrernteya rojavayî",
"zah": "zangwalî",
"ssl": "sisaalayiya rojavayî",
"mpc": "mangarayî",
"qun": "kuynaultî",
"gyd": "kayardildî",
"ikx": "îkî",
"dij": "daiyî",
"bwq": "boboyiya başûrî",
"aum": "asu",
"axb": "abipon",
"zac": "zapotekiya ocotlan",
"xwk": "wangkumarayî",
"nzi": "nzimayî",
"mzv": "manzayî",
"kwe": "kwerbayî",
"sjk": "kemi samî",
"gmv": "gamoyî",
"bkt": "bolokî",
"atw": "atsugewî",
"zpk": "zapotekiya tlacolulita",
"wbe": "waritaî",
"ray": "rapayî",
"ojs": "ojibweyiya severnê",
"zmd": "maridanî",
"kxz": "kerewoyî",
"irr": "îrî",
"gbr": "gbagyiyî",
"bzx": "bozoyiya hainyaxo",
"aya": "awar",
"res": "reşeyî",
"oin": "oneyiya inebu",
"mwe": "mwerayî",
"ite": "îteneyî",
"gaf": "gendeyî",
"gdl": "diraşayî",
"nfd": "ahwayî",
"tlm": "tolomakoyî",
"mgv": "matengoyî",
"gqn": "kinikinaoyî",
"jah": "jah hutî",
"gog": "gogoyî",
"doy": "dompoyî",
"bfj": "bafanjî",
"xwc": "wokkonî",
"tei": "torricellî",
"sha": "şall-zwallî",
"stn": "owayî",
"emy": "mayaniya epigrafîk",
"azd": "durango nahuatliya rojavayî",
"bls": "balaesang",
"dul": "alabat island agtayî",
"xan": "xamtangayî",
"plk": "şinayiya kohistanî",
"sai-sac": "sacatayî",
"apf": "pahanan agta",
"npa": "nar phuyî",
"mfn": "mbembeyiya çema crossê",
"czk": "knaanicî",
"jow": "jowuluyî",
"cbb": "cabiyariyî",
"bpq": "banda malayî",
"sit-tsh": "tşobdunî",
"sbe": "salibayî (Gîneya nû)",
"mfj": "mefeleyî",
"lok": "lokoyî",
"kdu": "kadaruyî",
"ekp": "ekpeyeyî",
"bap": "bantawa",
"ali": "amaimonî",
"ynu": "yahunayî",
"tuf": "tuneboyiya navendî",
"ppu": "paporayî",
"dcr": "negerhollandsî",
"meq": "mereyî",
"nai-knm": "konomihuyî",
"ijn": "kalabariyî",
"hla": "haliayî",
"alm": "amblongî",
"ynn": "yanayî",
"ntj": "ngaanyatjarrayî",
"kbq": "kamanoyî",
"anm": "analî",
"ukg": "ukurigumayî",
"mav": "satere-maweyî",
"mfz": "mabaanî",
"kqq": "krenakî",
"hns": "hindustaniyiya karibî",
"taf": "tapirapeyî",
"mig": "mixteciya san miguel el grande",
"kni": "kanufiyî",
"sai-chp": "çapacurayî",
"cko": "anufoyî",
"wnu": "usanî",
"spe": "sepayî (Gîneya nû)",
"xrw": "karawayî",
"bnt-cmw": "çimwiinî",
"vam": "vanimoyî",
"sjr": "siar-lakî",
"poo": "pomoyiya navendî",
"mlz": "malaynonî",
"xks": "kumbewahayî",
"ldb": "idunî",
"tqb": "tembeyî",
"nai-sin": "sinacantanî",
"nur": "nuguriayî",
"mqx": "mamujuyî",
"unn": "kurnayî",
"bcn": "bîbaalî",
"ttr": "terayî",
"wkd": "moyî",
"kzl": "kayeliyî",
"ntk": "îkomayî",
"gdg": "ga'dangî",
"gbj": "bodo gadabayî",
"abi": "abîdjî",
"zao": "zapotekiya ozolotepec",
"sai-wnm": "wanhamî",
"thp": "thompsonî",
"cwe": "kwereyî",
"xbn": "kenaboî",
"gnq": "ganayî",
"tcc": "datoogayî",
"ztg": "zapotekiya xanaguia",
"zmj": "maridjabinî",
"lak": "lakayî",
"kvr": "kerînçî",
"ggu": "gbanî",
"anv": "denya",
"aof": "bragat",
"awr": "awera",
"zhb": "zhabayî",
"www": "wawayî",
"stb": "subaneniya bakurî",
"ret": "rettayî",
"oon": "öngeyî",
"mlv": "mwotlapî",
"mzr": "marúboyî",
"lag": "langiyî",
"kxc": "khonsoyî",
"itx": "îtikî",
"bja": "budza",
"ayu": "ayuyî",
"tba": "aikanayî",
"txm": "tominî",
"orx": "oroyî",
"nhq": "nahwatliya huaxcaleca",
"mpq": "matisî",
"kfk": "kinnauriyî",
"kzh": "dongolawiyî",
"bck": "bunabayî",
"ksf": "bafiayî"
},
"numberLabels": {
"y": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"s": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"d": {
"cat": "dualia tantum",
"display": "\u003Cabbr title=\"hejmar dual\"\u003Edu\u003C/abbr\u003E"
},
"p": {
"cat": "POS pirjimar",
"display": "\u003Cabbr title=\"pirjimar\"\u003Ep\u003C/abbr\u003E"
},
"p+": {
"cat": "POS axlebî pirjimar",
"display": "axlebî pirjimar"
},
"tp": {
"cat": "POS tenê pirjimar",
"display": "tenê pirjimar"
},
"j": {
"cat": "POS jimarbar",
"display": "[[jimarbar]]"
},
"j+": {
"cat": "POS axlebî jimarbar",
"display": "axlebî jimarbar"
},
"j/nj": {
"cat": "POS jimarbar û nejimarbar",
"display": "[[jimarbar]] û [[nejimarbar]]"
},
"nj": {
"cat": "POS nejimarbar",
"display": "[[nejimarbar]]"
},
"nj+": {
"cat": "POS axlebî nejimarbar",
"display": "axlebî [[nejimarbar]]"
},
"hj!": {
"display": "hejmar nepeyitandî ye"
},
"hj?": {
"req": true,
"display": "hejmar hewce ye"
}
}
}
4is2h0cjoo5psh9tpxrgynw4pi57fgt
Wîkîferheng:Xebat
4
1357857
6334103
6333917
2026-08-16T14:45:13Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334103
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
d0go9qd02ah5qfdnexspz0s9qul7v24
6334105
6334103
2026-08-16T14:48:17Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334105
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê:". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
cnrlhfcx4p3mqtf16v0xpso5pzzxs3h
6334106
6334105
2026-08-16T14:54:19Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334106
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
rx1fia9l6r7urv1p7tn7dzt9kcub694
6334107
6334106
2026-08-16T14:56:22Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334107
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
0rwx0p1iw9sc9a6111undxmm5isz7vb
6334108
6334107
2026-08-16T14:57:19Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334108
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
2yi1ouv42vaa78ud3dqc9nx5bna057r
6334110
6334108
2026-08-16T15:02:26Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334110
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code> û <code>borî=</code>. Bnr, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
6mrwvkwjebgf6dpssm3pfxezn9yz9na
6334111
6334110
2026-08-16T15:04:07Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334111
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code> û <code>borî=</code>. <code>subj=</code>Bnr, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
tsjxwfa39dgwlxjs9iht0ifo27tyvat
6334112
6334111
2026-08-16T15:06:17Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334112
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code> û <code>borî=</code>. <code>subj=</code>Bnr, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
jzd0eatzlwbjxzq02dsnhyithy2cvgi
6334113
6334112
2026-08-16T15:08:27Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334113
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
mbbaj6fqsjsm6dnm8uadp56xxjb99m1
6334116
6334113
2026-08-16T15:17:37Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334116
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
22qjk5yw6sip3its7oic1f84m25ty2j
6334117
6334116
2026-08-16T15:19:03Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334117
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] wisa çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
l5i4noq0w4dfptq2a3etruqtor3lxhk
6334118
6334117
2026-08-16T15:19:24Z
Wikihez
7344
tam sirûcî be :D
6334118
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
je845052veixjufud8vy3mohbreq1ea
6334121
6334118
2026-08-16T15:50:08Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334121
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
jg3hr887uktfd7ksxgxnasmdkmr681u
6334126
6334121
2026-08-16T16:32:00Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334126
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
5cg7u6x7vjym1t59zc0nkh0eydwcfw0
6334162
6334126
2026-08-16T17:45:37Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334162
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina foramta proptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
oe6cfqhtxxd6b0z19345kuffu1s1jwn
6334163
6334162
2026-08-16T17:46:57Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334163
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
5kdv258xwk2w2f0erce553j0c4j10r8
6334167
6334163
2026-08-16T17:58:33Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334167
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-yên înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
09mrerrvq2jcw8vd1t84vgsfrvslpla
6334170
6334167
2026-08-16T18:00:25Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334170
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
mwx15k8f01yl9uw7na06vf07h465fkm
6334173
6334170
2026-08-16T18:04:27Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334173
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
ddq4ncahfq1p7ic0i66yx7uk9aclvup
6334175
6334173
2026-08-16T18:06:07Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334175
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
3ubjy5x2c4fwuzpyh04u8572zvti160
6334176
6334175
2026-08-16T18:07:06Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334176
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
6y77ftk6yjusj3ieeogkln00sp4gu4y
6334178
6334176
2026-08-16T18:14:29Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334178
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
mxumjsm9m4tfrgckrtpgdvrvjweqz8l
6334180
6334178
2026-08-16T18:21:56Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334180
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
:::::::::::::::::::) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
c4j4fyp2wa0nrp3abqmzwrfizqwmdnn
6334181
6334180
2026-08-16T18:22:21Z
Wikihez
7344
6334181
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
42pm07ngduo74l7nlf4cbsrgodttfjw
6334206
6334181
2026-08-16T20:03:17Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334206
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
d5wioa1ub7pok5nj83bjp4yx3d7xnzv
6334207
6334206
2026-08-16T20:03:35Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334207
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
iwvkijr6tl1lu7et2ksofkwfdp6t99n
6334214
6334207
2026-08-16T20:25:46Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334214
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min zû bikaranîna wî fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
93k99121qkk8b8s566zn6etif4sjw8z
6334215
6334214
2026-08-16T20:27:06Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334215
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
ffly6359geutpo493xv3tljvhy7qv4t
6334216
6334215
2026-08-16T20:30:04Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334216
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Belkî (nizanim) şûnda dema bilanî dema feramanî were nivîsandin. Çend caran wekî dema niha nayê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:30, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
jvz687nn1jzekwvw4b6cjjm4fqnu7or
6334217
6334216
2026-08-16T20:31:01Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334217
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Belkî (nizanim) şûnda dema bilanî dema feramanî were nivîsandin. Çend caran wekî dema bê nayê çêkirin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:30, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
81zswudzjibuy4rccjk1q85rbg1qtmd
6334218
6334217
2026-08-16T20:33:00Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ bes e :) em nizanin bisekinin
6334218
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
ffly6359geutpo493xv3tljvhy7qv4t
6334219
6334218
2026-08-16T20:34:12Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334219
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
rnt6r8d433nnq3okij6ig9de3x9ec8c
6334222
6334219
2026-08-16T20:45:05Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334222
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
jj435vzejnatveuq6chtexacyoxd59z
6334223
6334222
2026-08-16T20:45:37Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334223
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hetim wisa ye. Mînak bi devoka rojhilatî: ewd dê bêt (dema bê) û bibê (dema fermanî). Li vira dema ferma bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
hmizxlhfzd08q52qhai7410ctgqdaib
6334224
6334223
2026-08-16T20:47:24Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334224
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
t87t3dj1yiing8wk32ogueije41n9op
6334225
6334224
2026-08-16T20:49:03Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334225
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwatin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
klg512o1sioyk4y5afd7z8ajs8ibz8v
6334226
6334225
2026-08-16T20:49:33Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334226
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
qxs59unw95ycegq4oenyos6m4td43mn
6334228
6334226
2026-08-16T20:53:33Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334228
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
6ybdf7zvurzmw93afand6u3hgiwrgc7
6334229
6334228
2026-08-16T20:55:03Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334229
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
aoho8x1yj4jeracvbet9xhm51oycyzq
6334236
6334229
2026-08-16T21:06:11Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334236
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
4e2kwsehx3ulsipwovwbjthqobji9a6
6334240
6334236
2026-08-16T21:23:03Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334240
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandinê reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şans e me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
08yx59ubj48dlmubmm30mljb9rinao8
6334241
6334240
2026-08-16T21:23:44Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334241
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şans e me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
9a0vdp73ilh1ik2x0tyiic2lstr5ust
6334242
6334241
2026-08-16T21:33:17Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */
6334242
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
2d01t7tiom51ajiz7wwoeatovlzyhz2
6334247
6334242
2026-08-16T22:05:39Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334247
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
qu0bpelrnpg6bwqtvfwlqxeosyk30z5
6334248
6334247
2026-08-16T22:07:37Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334248
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
6y8a2oilcz1vh0hj3fbdra4y894bd86
6334249
6334248
2026-08-16T22:11:08Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334249
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::::Tiştên ku lazim in were guhartin:
::::::::::::::::::::::::::::::* Placeholdera qismê "Tewandina "kelîme" binivîse"
::::::::::::::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
031ml1r63yj7ofdy59y88btdzwjcg09
6334250
6334249
2026-08-16T22:11:45Z
Wikihez
7344
6334250
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
l7lxzib2bo4a0wfr48gegeqp1gaq213
6334251
6334250
2026-08-16T22:14:33Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334251
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
6z6cc746duv63n4uzpy9tzngxhvhykl
6334252
6334251
2026-08-16T22:16:12Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334252
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
c0z9z0miwn69xap0l5op3rk6f0uicfs
6334253
6334252
2026-08-16T22:22:46Z
Ghybu
1393
/* MediaWiki:Gadget-start.js */ Bersiv
6334253
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
dabvbz5y35o7axrs9onfqw5cx87bl5r
6334254
6334253
2026-08-16T22:23:43Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334254
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
j4o45vpco54mt95mmzhy3knpnwmc3gr
6334255
6334254
2026-08-16T22:30:33Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334255
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
29xc6yc23e9ggwlsvdkhwb0ctuluc4n
6334256
6334255
2026-08-16T22:31:21Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334256
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
kagcrs7bcev8gytdjwnym5gq1qa6hbq
6334257
6334256
2026-08-16T22:33:10Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334257
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
::::::Pill-ên Hevmane/Dijmane etc. lazim in di derveyî qutiya kelîmeyê de xuya bibin piştî nivîsandinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:33, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
1iaipswugag7rxtqwrd0sa0q8gkgtt2
6334258
6334257
2026-08-16T22:34:01Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334258
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
::::::Pill-ên Hevmane/Dijmane etc. lazim in di derveyî qutiya kelîmeyê de xuya bibin piştî nivîsandinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:33, 16 tebax 2026 (UTC)
:::::::"- beşek hilbijêre -" bike "- binebeşên din hilbijêre -" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:34, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
heb536253e1hz3ijk7y39027407evna
6334259
6334258
2026-08-16T22:35:43Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334259
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
::::::Pill-ên Hevmane/Dijmane etc. lazim in di derveyî qutiya kelîmeyê de xuya bibin piştî nivîsandinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:33, 16 tebax 2026 (UTC)
:::::::"- beşek hilbijêre -" bike "- binebeşên din hilbijêre -" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:34, 16 tebax 2026 (UTC)
::::::::Çavkanî section dropdown linebreak dixe berîya "* şablon" lazim e linebreak piştî navê şablonê were. Îkonên lînk û înfo li vir jî naxebitin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:35, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
hye7vg6k0yayloohika8lxo8s9rih6w
6334260
6334259
2026-08-16T22:36:52Z
Wikihez
7344
/* MediaWiki:Gadget-start.js */ Bersiv
6334260
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
::::::Pill-ên Hevmane/Dijmane etc. lazim in di derveyî qutiya kelîmeyê de xuya bibin piştî nivîsandinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:33, 16 tebax 2026 (UTC)
:::::::"- beşek hilbijêre -" bike "- binebeşên din hilbijêre -" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:34, 16 tebax 2026 (UTC)
::::::::Çavkanî section dropdown linebreak dixe berîya "* şablon" lazim e linebreak piştî navê şablonê were. Îkonên lînk û înfo li vir jî naxebitin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:35, 16 tebax 2026 (UTC)
:::::::::"Binêre herwiha (bnr-h)" bike "Herwiha binêre (h-bnr)"
:::::::::"Şablona bnr-h li qismê herî jor a rûpelê" bike "Şablona h-bnr li qismê herî jor a rûpelê" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:36, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
byxqrtqy5msjkbcevbqcq7ziyq0cyxd
6334301
6334260
2026-08-17T11:07:14Z
Wikihez
7344
/* Şablon:jêder */ beşeke nû
6334301
wikitext
text/x-wiki
{{/ser}}
= Karên ferhengî =
== Mane ==
* [[:Kategorî:Madeyên bê beşa mane]]
** [[:Kategorî:Madeyên bê beşa mane bi kurmancî]]
Li ser van rûpelan ev made hene ku maneyan wan nehatine nivîsin û divê werin nivîsin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Etîmolojî ==
*[[:Kategorî:Daxwazên etîmolojiyê]]
**[[:Kategorî:Daxwazên etîmolojiyê bi kurmancî]]
Li ser van rûpelan ev made hene ku etîmolojiya wan hîn nehatine nivîsin. Ji bo alîkariyê binêre [[WF:Etîmolojî]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Cureyê rêzimanî ==
* [[:Kategorî:Gotarên bê kategoriya rêzimanî]]
* [[:Kategorî:Gotarên bê kategoriya rêzimanî bi kurmancî]]
Li ser van rûpelan ev made hene ku cureyên wan yên rêzimanî hîn nehatine nivîsin. Ji bo alîkariyê binêre [[Alîkarî:Cureyên peyvan]] —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Kontrol ==
* [[:Kategorî:Kontrol]]
** [[:Kategorî:Kontrola maneyê]]
Ev madde bi botê hatine çêkirin, yek bi yek divê werin kontrolkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
= Şablon =
== IPA ==
* {{ş|IPA}} divê [[Modul:IPA]] bi kar bîne. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Deng ==
Kurdîkirina belgekirina {{ş|deng}} lazim e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:06, 19 tîrmeh 2026 (UTC)
= Modul =
== Modul:form of/templates ==
* [[Modul:form of/templates]]: Divê ev modul were rojanekirin û xeletiyên berê wekî ([[hevalê]]) werin rastkirin. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
*:Merheba @[[Bikarhêner:Ghybu|Ghybu]] Te gotibû di vê modulê de xeletî hene, jkx tu dikarî li vir binivîsî çi kêmasî hene, daku wextê rojane dikim ez ji bîr nekim? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:16, 21 tîrmeh 2026 (UTC)
*::Silav. Nizanim ji bîra min çûye... Herwiha ji bo çêkirina formên navdêran zanînên cîns û tîpa dawî bes e:
*::* (-ê, mê) => çemandî, bangkirin
*::* (-ê, nêr) => îzafe
*::* (-ê, mê û nêr) => îzafe (nêr), bangkirin (mê), çemandî (mê)
*::* (-a) => îzafe (mê)
*::* (-ekî) => îzafe û çemandî (nêr, nebinavkirî)
*::* (-eke) = > îzafe (mê, nebinavkirî)
*::* ...
*:: <code>if find(mw.title.getCurrentTitle().subpageText, 'ekî$') then ... end</code>
*::<code><nowiki>{{ku-tewîn-navForm|<navdêr>|<zayend>}</nowiki></code>
*::Tablo û nivîs di yekcarî de tên çêkirin...
*::Feqet problem lêzêdekirina mînakan e. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:34, 21 tîrmeh 2026 (UTC)
*:::Lêzêdekirina kîjan nimûneyan? Ev koda js ji bo çi ye? Qisûrê nenêre min fêm nekir :/ [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:13, 21 tîrmeh 2026 (UTC)
*::::Li vira ([[hevalê#Formeke navdêrê]]) di bin maneyên 1. û 2. mînak nayên tevlî kirin :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:18, 21 tîrmeh 2026 (UTC)
*:::::Ev tercîh e, ne mecbûrî ye, em dikarin [https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&diff=prev&oldid=6276350 wisa] bikin. Wekî din? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:29, 21 tîrmeh 2026 (UTC)
*::::::Min li jor tênê got ku tenê parametreyên "navdêr" û "zayend" hewce nee. Bi '''[//ku.wiktionary.org/w/index.php?title=heval%C3%AA&curid=969734&diff=6276353&oldid=6276350 vî]''' awayî jî tê çêkirin. Parametreyên din ne hewce ne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:38, 21 tîrmeh 2026 (UTC)
*:::::::Ji bo wê şabloneke din lazim e, bila ev şablon wekî îngilîzî bimîne em ji bo kurmancî yekî din çêkin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:42, 21 tîrmeh 2026 (UTC)
*::::::::[https://ku.wiktionary.org/w/index.php?title=heval%C3%AA&oldid=6276885 wisa] çawa ye? Bi min nivîsîna nimûneyan zêde ne lazim e ji ber ku ti kes serê xwe pê naêşîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:46, 23 tîrmeh 2026 (UTC)
*:::::::::@[[Bikarhêner:Ghybu|Ghybu]] Vê şablonê bi kar bînin an na? Navê şablonê kê çi be? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:07, 24 tîrmeh 2026 (UTC)
== Modul:columns ==
* [[Modul:columns]] were rojanekirin û {{ş|kol3}} bila lînka ''nîşan bide'' deyne beriya sernavê, kolê di qutiya rengî de bila nebe. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Modul:etymon ==
* [[Modul:etymon kevn]]: Rojanekirin hewce ye. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Modul:ziman/code to canonical name]] ==
Belgekirina skrîptê û js divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:31, 22 tîrmeh 2026 (UTC)
:* [[MediaWiki:UpdateLanguageNameAndCode.js]]
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:50, 22 tîrmeh 2026 (UTC)
::* [[Modul:ziman/print]]
::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:52, 22 tîrmeh 2026 (UTC)
:::*[[Modul:ziman/şablon]]
:::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:56, 22 tîrmeh 2026 (UTC)
::::* [[MediaWiki:Gadget-translation editor.js/ziman.json]]
::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:06, 22 tîrmeh 2026 (UTC)
== [[Modul:quote]] ==
Piştî ev modul hate hazirkirin, lazim e navên jegirtinan werin rastkirin û li hemûyan {{param|jêgirtin}} were zêdekirin. Bnr. [[Gotûbêja şablonê:Jêgirtin/Ehmedê Xanî/Mem û Zîn]] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:27, 5 tebax 2026 (UTC)
::Ev modul hema bêje hazir e. Hûn li ser van madeyan çi difikirin:
# {{para|tarîx}} lazim e full date be eger na, lazim e {{para|sal}} were bikaranîn. Feqat "'''2021''' kanûna pêşîn 23" bi vî şiklî bi min ne problem e çnkî derdê en.wikt "attestation" e delîla herî pêşî jî sal e.
#: {{demo|sep=wisa xuya dibe<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|tarîx=23 December 2021|nivîskar=Harry Hobbs; George Williams|sernav=Micronations and the Search for Sovereignty|url=https://www.cambridge.org/core/books/micronations-and-the-search-for-sovereignty/B0414A3A322695D9EE71E4CDDFDA06DE|weşanxane=Cambridge University Press|rûpel=155|isbn=978-1-009-15012-5|jêgirtin=jêgirtin.|t=-}}</nowiki>}}
#{{para|amadekar}}/{{para|edîtor}}/{{para|terciman}} bi şiklê edîtoriya, amadekariya, tercimekirina
#: {{demo|sep=dide<br>|<nowiki>{{jêder-kitêb/ceribandin|en
|sernav=Medical Surgical Nursing|serî=Juta's Manual of Nursing|cildê-seriyê=volume 4|edîtor=Sophie Mataniele Mogotlane; Joyce Mokoena; Motshedisi Eunice Chauke; Anne Young|sal=2005|weşanxane=Juta Academic|isbn=978-0702171840|rûpel=!12-29|jêgirtin=jêgirtin|t=-}}</nowiki>}}
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:09, 10 tebax 2026 (UTC)
:@[[Bikarhêner:Bikarhêner|Bikarhêner]] û @[[Bikarhêner:Ghybu|Ghybu]] Hîn gelek tişt hene divê îzah bikin û fikrê we pirs bikim, feqat îzahkirin pir zor e, em dest bi bikaranînê bikin û dûre tiştên ku hûn naxwazin em dikarin biguherînîn, dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 13:11, 10 tebax 2026 (UTC)
::Dibe, piştî çêbû, em di praktîkê de bibînin ku çawa ye. -- [[Bikarhêner:Bikarhêner|Bikarhêner]] ([[Gotûbêja bikarhêner:Bikarhêner|gotûbêj]]) 18:05, 10 tebax 2026 (UTC)
= Javascript =
== [[Mediawiki:Gadget-citoid-helper.js]] ==
Ji bo bi citoidê kopîkirina malûmatên çavkaniyan amûreke wisa lazim e. Xisûsen ji ber ku bi çend amûrên din dikare were bikaranîn.
Feqat çend problem hene, şablonên me de kêmasiya parametreyan gelek in û xerîteyên citoidê hîn nehatine çêkirin. Wana sererast bikin dûre vê amûrê çêkin, piştre [[MediaWiki:Gadget-start.js|start.js]] û [[MediaWiki:Gadget-wikt.add-examples.js|add examples.js]] were rojanekirin.
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:04, 19 tîrmeh 2026 (UTC)
:Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]], ez dibêjim karê Modul:quote zêde wext bistîne, bila em dest bi bikaranîna van amûran bikin dûre kêmasiyan em ê temam bikin înşela. Dikarî vana li cihên xwe yên rast kopî paste bikî?
# [[Special:PermanentLink/6291574|Bikarhêner:Wikihez/skrîpt/js/Gadget-citoid-helper.js]] --> [[Mediawiki:Gadget-citoid-helper.js]]
# Ya li jêr kopî bike qismê herî dawî ya AmûrênVeşartî li [[MediaWiki:Gadgets-definition]]:
## <code><nowiki>* citoid-helper[ResourceLoader|hidden|default|dependencies=mediawiki.util]|citoid-helper.js</nowiki></code>
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:58, 26 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-start.js]] ==
Rojanekirin hewce ye. Min gelek tişt lê zêde kir lê ez niha test dikime. Piştre em bar bikin:
* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:08, 17 tîrmeh 2026 (UTC)
:Min [[MediaWiki:Gadget-start/data.json|data.json]] çêkir ji bo ku bi hêsanî malûmatên heyî werin guhartin bêyî guhartina Javascriptê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:49, 18 tîrmeh 2026 (UTC)
:Xeletiyên ku ji bîr nekim rast bikim:
:* Wextê min çavkanî "kitêb" hilbijart û bi destî tije kir xeletiyeke [https://ku.wiktionary.org/w/index.php?title=xezete&diff=prev&oldid=6265392 wisa] derket her kelîme hate lînkkirin.
:* <code>html("⠿")</code> ev ne lazim e.
:* textarea ji bo çavkaniyên jêgirtinan xaseten yên sernav hîn jî ne draggable e.
:* pêşdîtina zindî checkbox wextê tê rakirin, pêşdîtin winda nabe.
:* Butona mobîlê ya vekirina formê di moda tarî de belî nabe, tenê wekî qutiyeke tarî xuya dibe.
:* Metna hişyarkirinê di guhartina rûpelên heyî de lazim e baştir bikim.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:51, 18 tîrmeh 2026 (UTC)
::Ev problem çêbûn, ez ê binêrim ka çi problemên din hene. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:15, 19 tîrmeh 2026 (UTC)
:::Çêke: Ji bo qismê çavkaniyê yek dikare API call û Kategorî:Çavkanî bi kar bîne ji bo ku şablon zêde bikî ji yên heyî. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:21, 19 tîrmeh 2026 (UTC)
::::Ev jî çêbû, min beşa çavkanî jî çêkir. Eger xeletî dernekeve û problema valahiya piştî etîketê ji bîr nekim, piştî rojanekirina amûrên linkify, û citoid em dikarin bar bikin. Ez ê ti tiştê nû îlawe nekim, zêde dirêj bû. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:14, 21 tîrmeh 2026 (UTC)
:::::Merheba @[[Bikarhêner:Bikarhêner|Bikarhêner]]. Dikarî wana bar bikî?
:::::* [[Bikarhêner:Wikihez/Gadget-start.js]] --> [[MediaWiki:Gadget-start.js]]
:::::* [[Bikarhêner:Wikihez/Gadget-start.css]] --> [[MediaWiki:Gadget-start.css]]
:::::* [[MediaWiki:Gadgets-definition]] wisa rojane bike:
:::::* start[ResourceLoader|default|package|dependencies=mediawiki.util,ext.gadget.StorageUtils,ext.gadget.citoid-helper]|start.js|start.css|start.js/data.json
:::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:39, 5 tebax 2026 (UTC)
{{şirove}} Dewama ''[[Special:PermanentLink/6333688|Gotûbêja bikarhêner:Rewiye tine#WF:Nû]]'' bila gotûbêja wî em spam nekin. :D
{{r|Ghybu}} dikarî [[Special:PermanentLink/6333689|vê htmlê]] binêrî claude rast fêm kiriye yan na? Daxîne û wek html qeyd bike. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:43, 14 tebax 2026 (UTC)
:Min seh wîkîyên din jî kir, li wîkiya fransî tiştekî wekî daxwaza min çêkirine (tiştên ku ne hewce ne wekî "tab" çêkirine an jî hatine vêşartin): [//fr.wiktionary.org/w/index.php?title=fdsdfdsf&action=edit&redlink=1]. Formata me yê berî jî bi vê lojîkê hatibû çêkirin, wekî de.wiktionary ([//de.wiktionary.org/w/index.php?title=sdfdsfsdfs&action=edit&preload=Vorlage:Formatvorlage_(Substantiv_vereinfacht)]) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:01, 14 tebax 2026 (UTC)
::PS: Dîsa jî Claude min rind fêm kiriye :)) Feqet min got ku beşên Mane", "Bilêvkirin" û "Beşên din" cem hev (wekî "tab") ". Û dema ku tu terî di nav "Besên din" Hevmane, Jê, ... li wira werin dayîn. Wêne/Resim û Nimûne jî di nav beşa ("tab") "Mane" [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:17, 14 tebax 2026 (UTC)
::Erê ya fr.wikt min jî dîtibû jixwe fikrê tab min ji wan girtibû :D (feqat min li cem wan tenê tab-a maneyê dît yên din nedît çawa tê vekirin nizanim) Berê hemû beş piştî maneyê bibûn, dûre min sê Tab çêkir, Bingehîn+Lînkkirin+Bikaranîn. Niha lazim e Tabeke din çêkim. Ya ku claude çêkiriye bnr. [[Special:PermanentLink/6333698#L-7]] ne baş e bnr. [[Special:PermanentLink/6333703|qiyaskirinê li ser vê htmlê]] Êdî ez Claude abuse dikime ji ber ku Pro ye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:26, 14 tebax 2026 (UTC)
:::Claude dibêje:
:::Good clarification — that's a real simplification, not just a relabel. Updated STEP_GROUPS from 5 tabs down to 3:
:::Mane — the definitions themselves, plus Wêne (image), now grouped in with it instead of sitting in a separate misc tab. Nimûne needed no change since it was already rendering inside each Mane sense block, not as its own step.
:::Bilêvkirin — unchanged, still just pronunciation.
:::Beşên din — everything else in one flat bucket: hevmane, jê, têkildar, herwiha binêre, formên alternatîv, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, tewandin, etîmolojî, zaravayên din, werger, and bnr-h — instead of being split across the old Bingehîn/Lînkkirin/Bikaranîn/Din tabs.
:::No DOM-order changes were needed this time — the fix from the blank-tab bug looks up the tab bar's position dynamically now, so it didn't matter that I regrouped which steps go where. Same two files, saved to your Wiki folder — worth another pass through all three tabs once you've pasted this in, same as last time. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:34, 14 tebax 2026 (UTC)
::::Mînak:
Ziman
---------------------------------------------
Cureyê rêzimanî (Navdêr)
Navdêr (hilbijêre) ┆ Cîns (hilbijêre) ┆ Jimar (hilbijêre)
- lêker -mê -yekjimar
- rengdêr -nêr -pirjimar
- ... -... -...
---------------------------------------------
Mane || Bilêvkirin || Beşên din
hilbijêre:
- mane - IPA - Hevmane
- nimûne - kîte - Dijmane
- wêne
Dema ku tu "Cureyê rêzimanî" dikî "Lêker" :
Cureyê rêzimanî (Lêker)
Lêker (hilbijêre) ┆ Cure (hilbijêre)
- navdêr -rehê niha
- rengdêr -rehê borî
- ... -...
---------------------------------------------
┆ : select/option ("tab" nîne, tê dîtin)<br>
|| : "tab"
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 19:45, 14 tebax 2026 (UTC)
'''Sê-stûnî ("┆" = ne tab), li gorî Cureyê rêzimanî:'''
{| class="wikitable"
! Cureyê rêzimanî !! Stûna 2 !! Stûna 3 !! Têbînî
|-
| Navdêr / Serenav
| Cîns (hilbijêre ▾: mê / nêr / ... — tenê heke ziman zayendê rêzimanî hebe)
| '''Jimar''' (?) — pirs li jêr
| Qadên "Forma mê/nêr/pirjimar/yekjimar" niha jî li vir hene — ma ev di "Jimar"-ê de tên tevlihev kirin, an cuda dimînin?
|-
| Rengdêr
| (heman m/n/p/y forman ku Navdêr distîne)
| Sûperlatîv (sûp) / Komparatîv (komp)
| Ne li ser her du nîgaran hatiye behs kirin — ma sê-stûnî li vir jî tê bikaranîn?
|-
| Lêker
| '''Cure''' = Cureyê lêkerê (hilbijêre ▾: gerguhêz / negerguhêz / xwerû-... / pêkhatî-... / hevedudanî-... / biwêjî-... — 10 vebijêrk)
| Di heman stûnê de: Rehê dema niha / Rehê dema borî / Rehê dema bilanî (her sê qadên nivîsandinê ne, ne hilbijêrk in)
| Ma "Cure" her sê rehan di xwe de digire wek yek stûn, an her yek stûneke cuda ye?
|-
| Tîp (herf/sembol)
| Girdêk (gir) / Hûrek (hûr)
| —
| Ne behs kiriye
|-
| Her cureyek, li gorî zimên
| Transliterasyon (tr) / Transkrîpsiyon (ts)
| —
| Bi zimên ve girêdayî ye, ne bi cureyê rêzimanî ve
|-
| Her cureyek, tenê ziman=japonî
| Hanja / Translit. hanja
| —
| Ne bi cureyê rêzimanî ve girêdayî ye
|-
| Her cureyek
| '''Tercîhên pêşketî''' (kolapsî): form, formaalt, nekatcure, nekatcins, nekatçendterm, serlînkneke, nekatpalîndrom, neformtr
| —
| Vebijarkî, ji bo hemû cureyan
|}
'''Tabên jêrîn — Mane ‖ Bilêvkirin ‖ Beşên din ("||" = tab):'''
{| class="wikitable"
! Mane !! Bilêvkirin !! Beşên din (hemû 18 hêman)
|-
| mane, nimûne, wêne
| şablonên bilêvkirinê (mînak: IPA, kîte)
| tewandin, etîmolojî, bi zaravayên din, werger, jê, têkildar, herwiha binêre, formên alternatîv, hevmane, dijmane, jornav, jêrnav, notên bikaranînê, bi alfabeyên din, dûnde, çavkanî, bêhtir bixwîne, bnr-h
|}
'''Pirsên vekirî:'''
# "Jimar" (yekjimar/pirjimar) — qadeke ku nîşan dide têketî bi xwe yekjimar e an pirjimar e. Ev cuda ye ji "Forma yekjimar (y)"/"Forma pirjimar (p)" ya heyî (ku forma DÎN a kelîmeyê tomar dike). Herdu bimînin, an werin yek kirin?
# Ma sê-stûnî tenê ji bo Navdêr/Lêker e, an ji bo Rengdêr/Tîp jî heman mantiq derbas dibe?
# Di "Beşên din" de: dixwazî pêşî navnîşek/hilbijartinek nîşan bide (17 hêmanên din jî tê de), û qad tenê piştî hilbijartinê xuya bibin?
:Tu dikarî start.js ji Tercîh/ Amûran rakî û bi test.js li Amûran herî taliyê ye biceribînî. Claude ya li jor hazir kir, Jimar ez dikarim îzah bikim. Feqat bi min di tab-a maneyê de lazim e Bi zaravayên din (heta wek werger her car bila li rûpelan were zêdekirin) û werger jî hebe.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:18, 14 tebax 2026 (UTC)
:;Navdêr: Tenê "cîns" û "jimar" bes e. "Forma mê", "Forma nêr", ... nayê fêm kirin. Bi min wekî "Tercîhên pêşketî" bi navkî din veşartî çêke.
:;Lêker: "Cureyê lêkerê" nayê fêm kirin, bi min pêşta "geguhez/negerguhêz" were hilbijartin, paşê jî form "hevdudanî/pêkatî/..." ji ber ku her kes vana nizanî... Dîsa jî tu dizanî.
:Bi min beşên "Hevmane" û dijmane jî bike de nav beşa Mane ("tab") ji ber ku pirr tên xwestin.
:Şûnda Wêne jî Nimûne/jêgirtin...
:Herwiha hemû bi rengê şîn hatine çêkirin, nizanim beşên din (===) bi rengê gewr an reş çito dibe? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:35, 14 tebax 2026 (UTC)
::Nimûne/jêgirtin, hevmane, Dijmane li binê her maneyê hene. ji ber ku wana bi maneyê ve girêdayî ne lazim e wisa bimîne feqat belkî şiklekî baştir bibînin. Hînbûna bikaranîna formeke nû bi qasî hînbûna koda wîkiyê zor e heqîqeten... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:45, 14 tebax 2026 (UTC)
:::Min şaş fêm meke feqet te amûrek ji bo Wikihez çêkiriye :) Tu dixwazî ku hemû tiştên ku te çêkiriye tevlî vê Gadgetê bikî û bikarhênerên din jî bi kar bînin... Tu jî dizanî ku kesên ku tên vira çito peyv çêdikin: Ziman > Mane > Mînak > Hevmane > Dijmane ! Du Gadget çêke yek ji bo bikarhênerên ku hertim li vira ne, yekeke din jî ji bo bikarhênerên din (yên ku pirr nên vira).
:::Herwiha kesên ku hertim li vira ne bi Gadgetê gotar çênekin, ''copy/paste'' dikin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 14 tebax 2026 (UTC)
::::Min xwest her tişt li ser rêza xwe be ji bo ku hîn bibin çi diçe kû derê feqat bi vî şiklî jî zêde dirêj û tevlihev dibe. Loma ez ê wekî tu dibêjî bikim her tiştê din bila collapsed pêşketî be. Ê ku dixwaze pê lê ke û binêre çi dikare were zêdekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:26, 14 tebax 2026 (UTC)
:::::Niha baştir tê fêm kirin {{=)}} [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:01, 14 tebax 2026 (UTC)
::::::Min ji Opus 5 û Design Tool îstifade kiriye hema bêje milyonek token çûn :D pêşî dîzayn kir dûre dest pê kir wekî dîna bişuxile. Pereyê xwe %100 heq dike welleh :D feqat moda tarî ji bîr kiriye :D [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:12, 14 tebax 2026 (UTC)
:::::::Spas! Etîmolojî jî wekî "Bilêvkirin", di beşekê din de cem wê, were çêkirin (Mane|Etîmolojî|Bilêvkirin|Beşên din)? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:20, 14 tebax 2026 (UTC)
::::::::(Mane>Tewandin>Werger|Etîmolojî>Dûnde>|Bilêvkirin(formatek çawa?)>Resim|Eleqedar>Jê>Têkildar>Herwiha binêre|Beşên din)? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:29, 14 tebax 2026 (UTC)
:::::::::Nizanim, biceribîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 23:43, 14 tebax 2026 (UTC)
::::::::::"Li şablonekê bigere" ser Maneyê hatiye nivîsandin, nizanim rind tê fêm kirin an na? Min paşê fêm kir :)) Naskirina Wîkîferhengê hewce ye... Li bin were nivîsandin û "notek" hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:02, 15 tebax 2026 (UTC)
:::::::::::"Malûmatên pêşketî nîşan bide" jî bike ser "Hevman" ji ber ku pirr li jor e. Tu nizanî çi dike (nayê dîtin)... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 00:09, 15 tebax 2026 (UTC)
::::::::::::Dever/Devok jî were nivîsandin ? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:22, 15 tebax 2026 (UTC)
:::::::::::::Tistekî din, dema ku tu maneyê dinivîsî bi otomatîk li jêr mane "2" tê nivîsandin, ew jî ne baş e (pirranî ne hewce ye). Bi min wekî "tab" cem "Mane 1" were vekirin (MAne1|Mane 2|Mane 3), bi otomatîk û an jî bi tikandina ser "Maneyeke din lê zêde bike" (cem beşa Maneyê). An jî bin beşa Wergerê bi nivîsa "maneyeke din lê zêde bike" were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:44, 15 tebax 2026 (UTC)
::::::::::::::Ji bo mane2, mane3, ... problemek heye: "Beşên din" ji bo kîjan maneyê ye :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 01:49, 15 tebax 2026 (UTC)
:::::::::::::::Devok/dever ya rastî ji bo kurmancî herî muhîm e lê diçe qismê etîketê ew jî veşartî ye, Niha çawa çêbûye? Min butona +mane zêde bike îlawe kiriye. Beşên din ji bo hemû maneyan e, ji bo maneyên xisûsî lazim e yek qismê malûmatên zêdetir bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:01, 15 tebax 2026 (UTC)
::::::::::::::::mane1, man2, ... cem beşa "Mane" çito ye, wekî:
::::::::::::::::<poem>
:::::::::::::::: '''Mane''' || Etîmoljî || Eleqedar || Beşên din
:::::::::::::::: -----------------------------------------------------------------
:::::::::::::::: Mane 1 || '''Mane 2''' || + Mane zêde bike [[Wêne:OOjs UI icon trash-destructive.svg|18px]]
:::::::::::::::: <small>Maneya kelîmeyê binivîse. Mane otomatîk tê ...</small>
::::::::::::::::┌───────────────────────────────────┐
::::::::::::::::│ Kelîmeyên ji bo... │
::::::::::::::::└───────────────────────────────────┘
:::::::::::::::: -----------------------------------------------------------------
::::::::::::::::</poem>
::::::::::::::::Belkî çêkirin zor e... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:59, 15 tebax 2026 (UTC)
:::::::::::::::::Ev nimûne çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:05, 15 tebax 2026 (UTC)
::::::::::::::::::Te beşa "Bilêvkirin" kiriye cem "Beş1", erê. Li gor vê lojîkê "Etîmlojî" jî were cem beşa "Bilêvkirin", na? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:19, 15 tebax 2026 (UTC)
::::::::::::::::::Etîmolojî h4 e Bilêvkirin h3 loma min Bilêvkirin xiste cem "Beş1". Tu dibêjî îzaha qismê Maneyê "Maneya kelîmeyê binivîse. Mane otomatîk tê" li bin tabên Maneyê bin ne wisa? Pir ferq dike? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:23, 15 tebax 2026 (UTC)
:::::::::::::::::::Checkbox ji bo Etîmolojiya "Beş2" zêde bikim? Eger truthy be bila Etîmolojî h3 be û here pêşiya crz (cureyê rêzimanî em jî hinek kurtkirin bi kar bînin êdî) ? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:25, 15 tebax 2026 (UTC)
:::::::::::::::::::Na min tiştekî witira negot, min tenê ji bo fêmkirinê ew şemayê çêkir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:27, 15 tebax 2026 (UTC)
::::::::::::::::::::OK, niha rind bû :) "Maneyeke din zêde bike" çito ye? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:32, 15 tebax 2026 (UTC)
::::::::::::::::::::Temam wê demê her tişt çêbû... Însan hîn dikare gelek tişt zêde bike, feqat usage limits hene :D Min session-a 5 saeatî temam kir. Dûre ez ê wisa bikim wextê pê li taba Mane-Etîmolojî dikî here jor, bi vî aawayî piştî dagirtina her tiştî li jêr însan dikare bi hêsanî maneyên din binivîse. Pişhingî jî ez ê paqiya zimanî bikim, piştî hinek ceribandinan em nû bikin. Eger fikrên nû were dûre dîsa dikarin îlawe bikin :) [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:35, 15 tebax 2026 (UTC)
:::::::::::::::::::::Temam! Dema ku tu "Mane 2" an "Mane 3" bi şaşî nivîsand... çito tê/n jêbirin? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:47, 15 tebax 2026 (UTC)
::::::::::::::::::::::Vala bikî bes e, beşên vala dernakevin li outputê. butonek çêkin? li kû derê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:49, 15 tebax 2026 (UTC)
::::::::::::::::::::::Cem "Maneyeke din zêde bike" '''jêbibe''' were nivîsandin an jî wêneyê [[Wêne:OOjs UI icon trash-destructive.svg|22px]]. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:51, 15 tebax 2026 (UTC)
:::::::::::::::::::::::Mane 1 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] Mane 2 [[File:OOjs_UI_icon_trash-destructive.svg|16px]] +Maneyeke din zêde bike çawa dibe? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:58, 15 tebax 2026 (UTC)
::::::::::::::::::::::::Nizanim kîjan nîşan baş e: va jî heye: <code>✕</code> [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:03, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::Bi min tenê yek carê were nivîsandin ji ber ku tu bi şasî maneyên xwe jî jê dibirî (dema ku tu li ser maneyê ditikînî) :) Li cem "Beş zêde bike" jî were nivîsandin [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::Bi kurdmancî "Rehê dema bilanî (subj)" ne hewce ye. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:48, 15 tebax 2026 (UTC)
:::::::::::::::::::::::::::Lazim e pêşî em şablonan li gorî zimanan çêkin wekî en.wikt {{şx|en-lêker}} dûre li gorî parametreyên TemplateDatayê em qutiyan dikarin zêde an kêm bikin. Niha Malûmatên şablonên etîmolojî, bilêbkirin û guharto hhwd. ji templatedayê dîstinê wextê pê li butona (i) dikî, lê gelek ji wan ne xwedî TemplateData ne. Hîn karên din jî hene divê em bikin wekî sererastkirina şablonên jêgirtinê. Loma niha wisa bimîne. Em li gotûbêja amûrê beşa Daxwaz çêkin? tiştên ku tên bîra me em li wir binivîsin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:14, 15 tebax 2026 (UTC)
::::::::::::::::::::::::::::Min yên ku te gotine çêkir feqat bi min resma jêbirinê baş nebû, tenê ya talî tê jêbirin ne tiştekî normal e? Min Beşên din jî xiste nav Eleqedar, Beşên din ne hewce ye. Bila zêde tab jî nebin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:11, 15 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::}}
Îkona jêbirinê (min rind fêm nekir) li gor daxwaza xwe bike... Herwiha çend bug hene:
# Di nav maneyê de nivîs "zû" nayê nivîsandin.
# Dema ku tu beşê dikî "Navdêr", paşê jî "Lêker": wekî "Navdêr" dimîne. Tu ê li ser "Bilêvkirin" bitikînî.
-- [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:18, 15 tebax 2026 (UTC)
:Min 1 fêm kir, ji ber ku Werger û Bi zaravayên din tên updatekirin dereng dimîne, belkî li şûna wextê nivîsînê, em bikin piştî nivîsînê ew ê zûtir be.
:Ya 2an min nikaribû bibînim çawa ev problem derdikeve, li ku derê wekî Navdêr dimîne piştî guhartinê? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:40, 15 tebax 2026 (UTC)
::Min fêm kir lazim e li Bilêvkirinê pê lê bikî da ku biguhêre... Ez ê rast bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:53, 15 tebax 2026 (UTC)
:::Tiştekî din :) Formatên "Bilêvkirin" (kare) û "Beş1" (nîv-oval) ne wekhev in. Herwiha formatên "+ Beş zêde bike" û "+ Mane zêde bike" jî ne wekhev in. Tercihên te ne an jî bug e? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:37, 15 tebax 2026 (UTC)
::::Ev nîv-oval ji min re gelek ecêb tê. Yên din lazim in yek şekil bin wekî +Mane zêde bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:40, 15 tebax 2026 (UTC)
:::::Li şûna "Ev ne pênase ye — şablonek bi kar bîne" ez ê çi binivîsim? Ti tişt nayê bîra min ya ku dikare vê bikaranînê îzah bike. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:43, 15 tebax 2026 (UTC)
::::::Maneyê bi şablonan çêke (rastnivîs, şas nivîs, aweyekî din, ku-Latn, ...) :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:12, 15 tebax 2026 (UTC)
:::::::Bi min "rehê niha" û "rehê borî" were dîtin ji ber ku bê wan ew lêker nayê bikaranîn. Tiştekî pirr hewce ye... Te "Cureyê lêkerê:" nivîsandiye paşe jî bin wan "Rehên lêkerê (Lêker çito tê tewandin?):". Herwiha Beşa "Tewandin" hertim şablona "ku-tewîn-nav" dide.
:::::::(?) Dever/Dovok jî bin Manyê were nivîsandin: Ew pey li ku derê an di kîjan devokê de tê bikaranîn: ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:45, 16 tebax 2026 (UTC)
::::::::An jî di bin beşa "Tewandin"ê de vana pirs bike....r [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 14:56, 16 tebax 2026 (UTC)
::::::::Herkes nikare "rehê niha" û "rehê borî" bizane, eger em "dema niha" û "dema borî" bixwazin hêsantir tê fêmkirin û bi zimanên din jî tê bikaranîn. Feqat [[Modul:rêziman]] wana destek dide? Nayê bîra min... Ez ê dîzayna Dever/devok ji design toolê pirs bikim [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:57, 16 tebax 2026 (UTC)
:::::::::Erê, bi parametreyên <code>niha=</code>/<code>nihatr=</code> û <code>borî=</code>/<code>borîtr=</code>. <code>subj=</code>Bnr, {{q|fa|داشتن}}, {{q|ku|emilandin}}, {{q|ku|emilîn}}. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:02, 16 tebax 2026 (UTC)
::::::::::Wana dizanim, ew reh in, ez dibêjim forma dema niha, forma dema borî, em fniha, fbila û fborî çêkin? Bi tirkî, îngilîzî jî dikare were bikaranîn... dikim, bikim, kir. yapiyorum, yap, yapti, doing, do, did? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:06, 16 tebax 2026 (UTC)
:::::::::::Ahh, min niha fêm kir :) Paşê jî li gor wan em dikarin rehê lêrê çêkin... Na ew nehatine çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:17, 16 tebax 2026 (UTC)
:::::::::::min ceriband [https://ku.wiktionary.org/w/index.php?title=yapmak&diff=prev&oldid=6334114] werga çitonî ye? Ez dibêjim zêdetir kes dikare forma dema niha bizane û binivîse feqat herkes nikare reh fêm bike çi ye û çawa ye. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 15:19, 16 tebax 2026 (UTC)
::::::::::::Bi min qutiya nivîsa Maneyê wekî yê "Nimûne" mezin were çêkirin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:50, 16 tebax 2026 (UTC)
:::::::::::::Prompt-ên me pir zeîf in, lazim e pêşî jê re Promptek bidin çêkirin dûre herin qismê Codeê û promptê bizelîqînin {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:32, 16 tebax 2026 (UTC)
::::::::::::::Êdî AI promptên ne baş jî fêm dikin û serrerast dikin, pêşta hukirina formata promptan hewce bû... Pirr pirs hatine pirs kirin; niha, ew dizanin kîjan rind e û tu çi dixwazî... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:45, 16 tebax 2026 (UTC)
:::::::::::::::Format niha baş e feqet ew Gadjet ji bo kesên ku debî-ya înternetê yên wan hêdî ye rind dişuxile? [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 17:58, 16 tebax 2026 (UTC)
::::::::::::::::(ji bo lêkeran) Bi min, bin "Lêker derbasdar e?" were nivîsandin wekî "Tewandina lêkerê". Pêş qutiyan jî "ez" an "min" (borî) ( li gor curyê lêkerê were nivîsandin... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:04, 16 tebax 2026 (UTC)
::::::::::::::::In this sentence, debî means "(data) throughput" or "bandwidth" — internet connection speed. It's a loanword from French débit (as in débit internet), which is used in Kurdish tech/web contexts for how fast a connection is.
::::::::::::::::So the phrase "kesên ku debî-yên înternetê yên wan hêdî ye" translates to "people whose internet speed/bandwidth is slow" — i.e., people on slow connections.
::::::::::::::::The full sentence is Ghybu asking: "The format is good now, but does this Gadget run well for people with slow internet connections?" — essentially a performance/loading-speed concern, not a formatting one.
::::::::::::::::If you want, I can look at what might be slowing the gadget down for low-bandwidth users (e.g., how many separate resources it loads, any heavy assets) and suggest something before you reply to them.
::::::::::::::::yes take a look
::::::::::::::::Read 6 files, ran 2 commands, searched 2 patterns
::::::::::::::::I dug into what actually gets downloaded on a fresh load of the wizard. Breakdown of the four files:
::::::::::::::::File Size (raw / gzip)
::::::::::::::::start.js 270 KB / 77 KB
::::::::::::::::data.json 114 KB / 36 KB
::::::::::::::::styles.css 87 KB / 19 KB
::::::::::::::::util.js 16 KB / 6 KB
::::::::::::::::So roughly 490 KB of source, ~140 KB after gzip — that's the real number Ghybu is feeling on a slow connection, especially the first time they load the gadget after any update (MediaWiki caches it aggressively afterward via a versioned URL, so it's mainly a one-time-per-version cost, not every page load).
::::::::::::::::Two concrete findings:
::::::::::::::::A real, fixable bottleneck: the whole gadget's initialization is gated behind mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], ...) — meaning the wizard doesn't start rendering anything until both modules arrive. But icons-moderation is only there for the trash-can icon on the delete buttons, and those buttons don't even exist until someone has 2+ Mane or 2+ Beş sections. Right now every user pays that extra module round-trip before seeing the form at all, even though most people won't touch a delete button in the first few seconds. Deferring that one module load until the first trash button is actually built would let the form render sooner on a slow connection, for free.
::::::::::::::::Not really fixable without bigger surgery: data.json's "ziman" (language list) is 75 KB of its 114 KB — the single biggest chunk in the whole payload. But that's needed for the very first field in the wizard (the language picker), so lazy-loading it would just delay the first thing the person needs, not help. The only way to shrink this meaningfully would be turning the language field into a live-search that fetches matches instead of shipping the whole list upfront — a much bigger change, and probably not worth it unless load time is a serious complaint from multiple people.
::::::::::::::::Want me to make the icons-moderation fix? It's small and low-risk — I'd move that module load to fire lazily right before the first delete button is created instead of blocking the initial render. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 16 tebax 2026 (UTC)
:::::::::::::::::Erê :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 18:14, 16 tebax 2026 (UTC)
::::::::::::::::::-:) Em çend sed ziman hilbijêrin tenê wana nîşan bidin? Niha tenê yên ku lêmayên wan hene tên lîstekirin. nêzîkî 3k ziman
::::::::::::::::::[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:21, 16 tebax 2026 (UTC)
:::::::::::::::::::Bi min Jimar di tercîhên pêşketî de veşartî be. Zêde nayê bikaranîn. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
::::::::::::::::::::"Tewandina lêkerê" rast çêbû? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:03, 16 tebax 2026 (UTC)
:::::::::::::::::::::Erê jimar were veşartin. Tewandina lêkerê rind bûye feqet dema bilanî ji bo zazakî ye.
:::::::::::::::::::::Qutiya "Cureyê rêzimanî" pirr nêzîkî nivîsa "Lêker derbasdar e?" ye û dûr nivîsa jor e.
:::::::::::::::::::::Herwiha Gadjetê "Bi otomatîk tijî bike" pirr rind e feqet min bikaranîna wî zû fêm nekir... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:25, 16 tebax 2026 (UTC)
::::::::::::::::::::::Min "Çend caran wekî dema bê nayê çêkirin... " fêm nekir. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:34, 16 tebax 2026 (UTC)
:::::::::::::::::::::::"Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)" bi min lazim e pêşiya qutiya mane be, ji ber ku me devok, etîket kategorî zêde kir niha ecêb bûye [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
:::::::::::::::::::::::Bi formên standardê dema fermanî wekî forma sêyem (yekjimar) a dema bê ye (dema bê: ew ê/dê bibêje// fermanî: bibêje). Feqet di nav devokan de ne hertim wisa ye. Mînak bi devoka rojhilatî: ew dê bêt (dema bê) û bibê (dema fermanî). Li vira dema fermanî bi dema bê nayê çêkirin. Feqet ne hewce ye ji ber ku li jêr devok tê xwastin. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:45, 16 tebax 2026 (UTC)
::::::::::::::::::::::::An jî dema bê were xwastin, dema fermanî wekî yê standardê tê çêkirin. Min tevlî hev kir :)) . Bi min bes e Gadjetekî tewandinê nîne... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 20:49, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::Em bilanî bikin "fermanî" zêde serê xwe neêşînin, wekî din em ê deh qutiyên din çêkin :D Piştî hinek clean-upê jî em biweşînin ji bo niha bes e {{=D}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:55, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::Problem, bi rastî, forma bê ye :)) Di çend devokan de pêşgira "bi-" nayê bikaranîn (torî, badînî, ... [[Modul:ku-tewandin#L-298]] [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:06, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::Te tabloya tewandina reşwî dît? [[Bikarhêner:Kurdanparez/sandbox]]. Şanseke me heye ku ew nekirine standard :). Feqet pirr entresan e, formên mê û nêr jî hene. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 21:23, 16 tebax 2026 (UTC)
::::::::::::::::::::::::::::Qet behsa standardiyê meîne (meyîne?) :D Ji wî alî de gelek xemgîn im. Min ji bo kurmancî forma dema bê tevlî kir, fermanî ji bo zimanên din. Feqat Claude niha naxebite dibêje "Claude is temporarily unavailable" {{=D}} Niha divê çi were paqijkirin. Tiştekî kêm ma?
::::::::::::::::::::::::::::"All the code changes (jimar dropdown from data.json for every language, fdbe → fdbê rename with fdbêtr added, and the "Taybetmendî" heading removed) are done and verified on my end — nothing is lost" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:05, 16 tebax 2026 (UTC)
:::::::::::::::::::::::::::::Min placeholdera URL, DOI... guhart niha baştir tê fêmkirin înşela? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:07, 16 tebax 2026 (UTC)
{{outdent|::::::::::::::::::::::::::::::}}Tiştên ku lazim in were guhartin:
* Placeholdera qismê "Tewandina "kelîme" binivîse"
[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:11, 16 tebax 2026 (UTC)
:Divê ns 108 destek bide. Wextê ns 108 be berîya beşa zimanî {{şx|jinûvesazkirî|langcode}} were zêdekirin, wekî din her tişt eynî ne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:14, 16 tebax 2026 (UTC)
::"Ev mane li ku derê an..." bike "Bi vê maneyê li kû derê an..." [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:16, 16 tebax 2026 (UTC)
:::Ji bo forma bê kesê sêyem were xwastin (ew ê/dê ...) ji ber ku di forma standardê de wekî forma fermanî ye: (tu) ... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 22:22, 16 tebax 2026 (UTC)
:::Kitêb/kovar/websîte lazim e formata fireh kurt wekî [[MediaWiki:Gadget-wikt.add-examples.js]] bi kar bîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:23, 16 tebax 2026 (UTC)
::::Butonên lînk û înfo ya dropdowna Etîmolojî û Bilêvkirin naxebitin. Ya dropdowna şablonên Maneyê jî du heb hene, yek di nav dropdownê de ku naxebitin ya din piştî hilbijartinê xuya dibin ku ew jî ne hewce ne, tenê lazim e di nav qutiyê de be. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:30, 16 tebax 2026 (UTC)
:::::Di "Tercîhên min" de butona "Hemû cache paqij bike" lazim e cache-yên etîket, dever û kategoriyê jî paqij bike. Niha nake. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:31, 16 tebax 2026 (UTC)
::::::Pill-ên Hevmane/Dijmane etc. lazim in di derveyî qutiya kelîmeyê de xuya bibin piştî nivîsandinê. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:33, 16 tebax 2026 (UTC)
:::::::"- beşek hilbijêre -" bike "- binebeşên din hilbijêre -" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:34, 16 tebax 2026 (UTC)
::::::::Çavkanî section dropdown linebreak dixe berîya "* şablon" lazim e linebreak piştî navê şablonê were. Îkonên lînk û înfo li vir jî naxebitin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:35, 16 tebax 2026 (UTC)
:::::::::"Binêre herwiha (bnr-h)" bike "Herwiha binêre (h-bnr)"
:::::::::"Şablona bnr-h li qismê herî jor a rûpelê" bike "Şablona h-bnr li qismê herî jor a rûpelê" [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:36, 16 tebax 2026 (UTC)
== [[Mediawiki:Gadget-QQ.js]] ==
Ev amûr di şikle Minerva de. Her daîm vê xeletiyê dide:
<syntaxhighlight lang="js">
TypeError: Cannot read properties of null (reading 'addEventListener') at css () at runScript () at Array.<anonymous> () at flushCssBuffer ()
</syntaxhighlight> [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:29, 18 tîrmeh 2026 (UTC)
:* [[Bikarhêner:Wikihez/skrîpt/js/Gadget-QQ.js]] --> [[Mediawiki:Gadget-QQ.js]]
:* [[Bikarhêner:Wikihez/Gadget-QQ.css]] --> [[Mediawiki:Gadget-QQ.css]]
:Ez dibêjim çêbû lê ka çend roj biceribînim. Tiştên ku hatin sererastkirin:
:# Google Books API Key dikarî zêde bikî, dixe cookies û di gerokê de dimîne. Berê bê API Key dixebitiya lê êdî mecbûr e ku API Key-a te hebe, bidestxistina wê ne zêde zor e lê dîsa jî tevlihev e.
:# Êdî di mobîlê de dixebite, heta di mobîlê de xweştir xuya dike.
:# Moda tarî
:# OOUI dewsa jquery.ui
:# Li qutiyê çi dixwazî zêde bikî, dikare li gorî îsm, sifet, fiîl bitewîne. Lê ji bo fiîl tenê dikare çend heb inflection bîne.
:# Butona paqij bike lê zêde kir.
:# Çend paqijiyên jêgirtinên Google Kitêbê niha hene.
:[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 23:38, 18 tîrmeh 2026 (UTC)
::Tewandina fiîlan û xeletiya modulê lazim e were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:40, 21 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-wikt.add-examples.js]] ==
Min ev amûr hinek rojane kir feqat çend problem hene berî ku bar bikin.
Tiştên ku min guhart:
# Êdî li sayfeyên bi {{ş|maneid}} jî dixebite,
# Cihê di wîkîtekstê de yê ku jêgirtin were îlawekirin baştir bû,
# [[Mediawiki:Gadget-citoid-helper.js|citoid helper]] zêde kir ji bo ku bi hêsanî çavkanî bi riya doi, isbn an urlyê werin tijîkirin.
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.css]] --> [[MediaWiki:Gadget-wikt.add-examples.css]]
* [[Bikarhêner:Wikihez/Gadget-wikt.add-examples.js]] --> [[MediaWiki:Gadget-wikt.add-examples.js]]
- --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 00:50, 19 tîrmeh 2026 (UTC)
:Min "wikt." qismên cache kirinê ya wîkiya fransî bi [[MediaWiki:Gadget-StorageUtils.js]] guhartiye. loma ji bîr nekin wê amûrê li [[MediaWiki:Gadgets-definition]] zêde bikin. û navê amûrê jî biguhêrînin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:15, 23 tîrmeh 2026 (UTC)
::Berê li ser WF:ceribandin dixebitî. niha naxebite. Divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 22:10, 7 tebax 2026 (UTC)
== Gadget-translation editor ==
* [[MediaWiki:Gadget-translation editor.js]]: divê formata en.wîktê were bikaranîn ji ber ku me gelek tercime ji en.wîktê kopî kir û ev js hîn nikare formata wekî li jêr bi kar bîne.
<syntaxhighlight lang="wikitext">
* {{Z|sh}}:
*: Kirîlî:
*: Latînî
</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== [[Mediawiki:Gadget-linkify.js]] ==
* ''rûpeleke'' wekî<syntaxhighlight lang="wikitext">[[rûpeleke]]</syntaxhighlight> lînk dike, bi regexê belkî dikare wisa bike <syntaxhighlight lang="wikitext">[[rûpel]]eke</syntaxhighlight>
—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
* Amûreke din wekî [[User:Wikihez/Gadget-linkify.js|linkify]] lazim e ku herfên mezin biçûk bike ji bo kurmancî.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:{{r|Bikarhêner}} Merheba bira, dikarî viya jî bar bikî?:
* [[Special:PermanentLink/6291974|linkify]] --> [[Mediawiki:Gadget-linkify.js]]
Spas--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:42, 29 tîrmeh 2026 (UTC)
:Dûre [https://ku.wiktionary.org/wiki/MediaWiki:Gadget-start.js#L-283 li vir] wiktviLinkify bike kuLinkify xêra xwe [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:51, 29 tîrmeh 2026 (UTC)
::Min ji bîr kir lê zêde bikim niha nikare "Dİ" bike "di", xeletiyeke berbelav a di nivîsên kurdî de, herfên din ên wekî ll yan rr jî hewce ne werin zêdekirin. Dûre em zêde bikin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 21:42, 29 tîrmeh 2026 (UTC)
:::Min nû ferq kir, sayfeyên ku hîn nehatine çêkirin wîkîlînk nake. Problemeke mezin e divê were rastkirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:15, 30 tîrmeh 2026 (UTC)
::::Na, lînk dike, tiştekî din heye... [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:49, 31 tîrmeh 2026 (UTC)
== Amûra manayê ==
Amûreke din ji bo nivîsina maneyê wekî Nimûne zêde bike lazim e [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6276876 js] [https://ku.wiktionary.org/w/index.php?title=%C5%9Eablon:ceribandin2&oldid=6265629 şablon] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6265642 css] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:12, 23 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-jeder.js]] ==
@[[Bikarhêner:Ghybu|Ghybu]] Çavkaniyên jêderê di moda pêşdîtinê de jêderê vedişêre, ew jî min gelek eciz dike wextê çavkanî zêde dikim. Min ji Claude pirsî û kodan nîşanî wê da, wisa cewab da:
Yes — and this file actually explains the whole thing precisely, better than my earlier guess about extract-stripping.
What's happening: the CSS rule
<syntaxhighlight lang="css">
.jeder{
display:none;
}
</syntaxhighlight>
hides every citation by default, *unconditionally*. The only thing that ever makes it visible again is the gadget's JS: it finds each `.jeder`, builds the short "`— (year, author, +)`" summary from the `.j-pewist` fields, and either reveals the short version or — for citations with no `.j-pewist` fields — explicitly sets `display:inline` on the original.
So visibility of citations depends entirely on this JS running. In the normal article view (Image 1) it runs, so you see the collapsed "`(1918, E. Rehmî Ji Mala Hekariyan, Amadekar: M. E. Bozarslan, +)`" form. In whatever generated Image 2 — a hover-preview/extract/API render — gadget JS doesn't execute, so nothing ever removes the `display:none`, and the citation vanishes completely rather than degrading to "still visible, just not collapsed."
<b>The fix:</b> flip the default so the *un-collapsed* state is the safe fallback, and let JS do the hiding only after it successfully builds the short version. That way, any context without JS shows the full citation instead of nothing.
CSS — remove the blanket hide:
<syntaxhighlight lang="css">
.jeder {
/* intentionally no display:none here — see Gadget-... .js */
}
</syntaxhighlight>
JS — do the hiding from script, after the short form exists:
{{veşêre-ser}}
<syntaxhighlight lang="js">
// Ji bo şablonên [[Şablon:jêder]] hatiye nivîsandin.
// Parametreyên ku di navbera "<span class="j-pewist">...</span>" an "{{j-pêwîst|...}}" de ne venaşêre.
$(function() {
function nivisaKurtTevliBike(sund, nivisKurt) {
var zede = $('<a>+</a>')
.addClass('click')
.attr('href', '#')
.attr('title', 'zêdetir')
.css('font-weight', 'bold');
var kurt = $(' <a>↩</a>')
.addClass('click-kurt')
.attr('href', '#')
.attr('title', 'veşêre')
.css('font-weight', 'bold');
$('<span>' + nivisKurt + '</span>')
.append(zede).append(')')
.addClass('jeder-kurt')
.css('font-size', '0.85em')
.insertAfter(sund);
sund.append(' ').append(kurt);
// Only hide the full citation once the short summary actually exists.
// If this script never runs (no-JS clients, hover-preview extracts,
// action=render, etc.), the full citation stays visible instead of
// silently disappearing.
sund.css('display', 'none');
}
function vesere() {
$('.jeder').each(function() {
var elmTab = $(this).find('.j-pewist').clone();
if (elmTab.length) {
var elm = '';
for (var i = elmTab.length - 1; i >= 0; i--) {
elm = elmTab.eq(i).html() + ', ' + elm;
}
var nivisKurt = ' — (' + elm;
nivisaKurtTevliBike($(this), nivisKurt);
}
// else: no j-pewist fields to shorten — leave the full citation
// as it already renders (visible), nothing to do.
});
}
vesere();
$(".click").click(function(e) {
e.preventDefault();
$(this).parent('.jeder-kurt').css('display', 'none');
$(this).parent('.jeder-kurt').prev().css('display', 'inline');
});
$(".click-kurt").click(function(e) {
e.preventDefault();
$(this).parent('.jeder').css('display', 'none');
$(this).parent('.jeder').next().css('display', 'inline');
});
});
</syntaxhighlight>
Net effect:
- JS-enabled readers (normal article view): identical experience to today — collapsed short form with `+`/`↩` toggle.
- No-JS contexts (previews, extracts, screen readers before JS loads, print, etc.): full citation is shown instead of nothing — a real regression fix rather than a cosmetic one.
One caveat worth being upfront about: this fixes it *if* the preview context renders the gadget's HTML/CSS but doesn't execute gadget JS (which is the far more common case, and what this code strongly suggests is happening). If Image 2 instead comes from a pipeline that strips the `.jeder`/`.jegirtin-text` elements out of the HTML entirely before this CSS/JS even gets a chance to apply, no on-wiki CSS/JS change can help — that'd need the extract-generation config changed instead. Given what this gadget file shows, though, I'd bet on the CSS-hide explanation.
{{veşêre-bin}}
Em dûre biceribînin eger xeletî tinebe, vî tiştî jî rast bikin. --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:00, 23 tîrmeh 2026 (UTC)
:Ez APIya MediaWiki rind nizanim belkî Claude dizane:) Em nikarin bibêjin dema ku moda "''action=edit''" e fonksiyona <code>function vesere()</code> bi kar meyîne. [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 15:57, 24 tîrmeh 2026 (UTC)
::Na, di moda "edît"-ê de <code>var elmTab = null</code>... [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:13, 24 tîrmeh 2026 (UTC)
?:
<syntaxhighlight lang="js">
if (
mw.config.get('wgAction') === 'edit' ||
mw.config.get('wgAction') === 'submit'
){
var elmTab = [];
} else {
var elmTab = $(this).find('.j-pewist').clone();
}
</syntaxhighlight>
--[[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:31, 24 tîrmeh 2026 (UTC)
:Ez jî nizanim Claude çiqas ji mediawiki fêm dike lê heta niha ti tişt nîne ku min jê pirs kir û nikaribû çêke, bila wekî reklam nebe feqat Claude mikemmel e, dibêjim "li vir çima xeletiyeke wisa derdikeve" di dosyeya 2000 rêzî de diçe tiştê xelet tenê rast dike tiştekî din naguherîne. Chatgpt û aistudio qet wisa nakin, eynî dosyeyê deh cara dinivîsin, deh netîceyên cuda derdikevin :D
:Em dikarin vê amûrê wextê edit û submitê neaktîv bikin wekî <syntaxhighlight lang="js">if (!(action === "edit" || action === "submit")) {
:return;</syntaxhighlight> lê wê demê display:none wê çibe nizanim. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:23, 24 tîrmeh 2026 (UTC)
::display:none ne hewce ye ma jixwe di nav kodê [https://ku.wiktionary.org/w/index.php?title=MediaWiki:Gadget-jeder.js&oldid=6069025#L-49 L-49] de inline/none heye? [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:53, 24 tîrmeh 2026 (UTC)
:Min bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/vector-2022.js&diff=prev&oldid=6290816#L-28] ceriband feqat nexebitî. "display:none" di pêşdîtinê de dixebite feqat js naşixule. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 14:43, 25 tîrmeh 2026 (UTC)
::Kodên JSê pir hez nakim (pêşta hukurina APIya MWyê hewce bû) dîsa jî ez ê seh bikim :) [[Bikarhêner:Ghybu|Ghybu]] ([[Gotûbêja bikarhêner:Ghybu|gotûbêj]]) 16:49, 27 tîrmeh 2026 (UTC)
:::Ez niha bi vî şiklî [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/test.js&oldid=6290820] [https://ku.wiktionary.org/w/index.php?title=Bikarh%C3%AAner:Wikihez/common.css&oldid=6290821#L-8] bi kar tînim ti problem nîne. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:09, 27 tîrmeh 2026 (UTC)
== [[MediaWiki:Gadget-PagePreviews.js]] ==
Ez dibêjim em vê amûrê jî çêkin [[:en:MediaWiki:Gadget-PagePreviews.js]] --[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 10:56, 3 tebax 2026 (UTC)
= Moda tarî =
== [[Şablon:wîkîpediya]] ==
Rengê tarî de xirab e.--[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 19:36, 4 tebax 2026 (UTC)
= Bot =
Karên ku bi botê werin çêkirin ji kerema xwe li vir binivîsin.
== Kategorî:Biwêj ==
[[:Kategorî:Biwêj]] divê were paqijkirin, lîsteya madeyên {{ş|biwêj}} çêke, yên ku tê de bûn, kirin, man hene filtre bike yên din bi destî tê çêkirin.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Jêgirtin ==
Xistina jêgirtinên beşa [https://ku.wiktionary.org/w/index.php?search=insource%3A%2F%3D+*Ji+%5BWw%5D%C3%AAjey%C3%AA+*%3D%2F&title=Taybet%3AL%C3%AAger%C3%AEn&profile=advanced&fulltext=1&ns0=1&searchToken=8rh0bmw8dccsi7aynl6zli8uf ji wêjeyê] li beşa maneyê. —[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== Bota Toolforgeê ==
Botekî Toolforgeê lazim e ji bo ku xeletiyên biçûk wekî şablonên bi navên îngilîzî werin kurdîkirin yan jî valahiya beşan rast bike û hwd.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
:Min [[Bikarhêner:Wikihez/skrîpt/py/kuwiktkozmetik.py|paqijiyen kozmetîk]] hazir kir feqat hîn navên şablonan naguhêre. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 20:05, 4 tebax 2026 (UTC)
== Updatew ==
Bota [[Bikarhêner:Wikihez/skrîpt/py/updateW.py|updateW]] divê were rojanekirin. Yan jî yekî nû were çêkirin. Şablonên {{ş|W}} di beşa tercimeyan de sererast dike û zimanan rêz dike. Ya niha xeletiya navê zimanî dide.—[[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 16:17, 16 tîrmeh 2026 (UTC)
== sihir --> sêhr ==
Tercimeyên ''[[sêhr]]'' hemû dibêje ''sihir'' loma bi botê lazim e kirin ''sêhr''. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 17:43, 18 tîrmeh 2026 (UTC)
:Sihirbaz --> sêhrbaz [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:07, 18 tîrmeh 2026 (UTC)
== Tercimeyên dîroknas ==
Li tercimeyên [[dîroknas]], hevmaneyên wê hewce ye were îlawekirin. [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 18:44, 18 tîrmeh 2026 (UTC)
== Mewlûd ==
Sererastkirina jêgirtinên mewlûdê [https://ku.wiktionary.org/w/index.php?search=insource%3A%2Fw%5C%3AMela+His%C3%AAn%C3%AA+Batey%C3%AE%2F&title=Taybet%3AL%C3%AAger%C3%AEn&ns0=1&searchToken=8m0tguy3sqt7edr4sg0u8hedo] wekî [https://ku.wiktionary.org/w/index.php?title=dax%C3%AEdar&curid=284303&diff=6265851&oldid=6142285] [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 09:43, 20 tîrmeh 2026 (UTC)
== [[Şablon:jêder]] ==
Şablon jêder axlebî bi şiklê MM/YYYY sal tê de heye, bi botê em dikarin wana bixin {{para|sal}} [[Bikarhêner:Wikihez|Wikihez]] ([[Gotûbêja bikarhêner:Wikihez|gotûbêj]]) 11:07, 17 tebax 2026 (UTC)
ktatglfileljoymor6n7mgs7uk559di
MediaWiki:Gadget-linkify
8
1358212
6334099
6291982
2026-08-16T13:56:20Z
Wikihez
7344
6334099
wikitext
text/x-wiki
[[MediaWiki:Gadget-linkify.js|linkify.js]] — Amûrên wîkîlînkkirin, rakirina wîkîlînkan û MEZIN/biçûkkirina herfan li [[:mw:Extension:WikiEditor|toolbarê]] zêde dike.
lhzxswoosaranrjhcp4ipeerfnbi3ki
Bikarhêner:Balyozbot/kontrol/rêzkirina beşan
2
1358302
6334270
6334068
2026-08-16T23:29:04Z
Wikihez
7344
/* Nenas beş */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334270
wikitext
text/x-wiki
== Nenas beş ==
== Astengî di rêzkirinê de ==
* [[bariyer]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[biyang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[biyokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[biyoteknolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[botanîk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêemrî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêfermanî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêjename]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bêrîvan]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[bûk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[cilxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[derbûn]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[derkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[dualîtî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[gizing]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[hegemonî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[heste]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[heywannasî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[hezîran]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[hiyeroglîf]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[jêkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[kanûna paşîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[kefalet]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[komparatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[kronolojî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[melû]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[meriçîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[mexel]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[neqişandin]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nivistinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[nîlûfer]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nîsanok]]: astengî di rêzkirinê de ji ber binbeşa "Navê zanistî" (ne hate rêzkirin)
* [[peryod]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[petrokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[peyîz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[pifdank]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pismam]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pol]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[qap]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qefa]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qolanî]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[raber]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[raketinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rapelîn]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[razang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[razanxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[revandin]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rezber]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rûniştinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[serborî]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[surprîz]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[sûperlatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[tabloîd]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[texe]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[teyr]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[tirp]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[tîre]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[werkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[weşanxane]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[wuşe]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[xwarin]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[zirz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[zoolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zêrandin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[çiriya yekem]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[çîvçîv]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[şepilîn]]: astengî di rêzkirinê de ji ber binbeşa "Tewandin" (ne hate rêzkirin)
* [[şor]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şêl]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[şêwr]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paş guhê xwe ve neavêtin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paşguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[piştguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şehde]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[nahiye]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[zoolog]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[heywannas]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêdengahî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[aya]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bijîşk]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rolyef]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[mefreq]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
== Şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî ==
* [[dest]]: şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî, ji kerema xwe kontrol bike, beş: "Navdêr"
* [[pestandin]]: şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî, ji kerema xwe kontrol bike, beş: "Navdêr"
* [[vewêrtin]]: şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî, ji kerema xwe kontrol bike, beş: "Navdêr"
* [[xeriqandin]]: şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî, ji kerema xwe kontrol bike, beş: "Navdêr"
* [[şepilîn]]: şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî, ji kerema xwe kontrol bike, beş: "Navdêr"
3g46ksyfp3loffc7006f67vnlplucfp
6334272
6334270
2026-08-16T23:32:51Z
Wikihez
7344
/* Şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî */ (bi [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]]ê re)
6334272
wikitext
text/x-wiki
== Nenas beş ==
== Astengî di rêzkirinê de ==
* [[bariyer]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[biyang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[biyokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[biyoteknolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[botanîk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêemrî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêfermanî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêjename]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bêrîvan]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[bûk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[cilxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[derbûn]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[derkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[dualîtî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[gizing]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[hegemonî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[heste]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[heywannasî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[hezîran]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[hiyeroglîf]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[jêkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[kanûna paşîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[kefalet]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[komparatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[kronolojî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[melû]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[meriçîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[mexel]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[neqişandin]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nivistinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[nîlûfer]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nîsanok]]: astengî di rêzkirinê de ji ber binbeşa "Navê zanistî" (ne hate rêzkirin)
* [[peryod]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[petrokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[peyîz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[pifdank]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pismam]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pol]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[qap]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qefa]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qolanî]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[raber]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[raketinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rapelîn]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[razang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[razanxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[revandin]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rezber]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rûniştinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[serborî]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[surprîz]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[sûperlatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[tabloîd]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[texe]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[teyr]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[tirp]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[tîre]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[werkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[weşanxane]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[wuşe]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[xwarin]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[zirz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[zoolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zêrandin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[çiriya yekem]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[çîvçîv]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[şepilîn]]: astengî di rêzkirinê de ji ber binbeşa "Tewandin" (ne hate rêzkirin)
* [[şor]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şêl]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[şêwr]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paş guhê xwe ve neavêtin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paşguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[piştguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şehde]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[nahiye]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[zoolog]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[heywannas]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêdengahî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[aya]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bijîşk]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rolyef]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[mefreq]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
== Şablona ku-tewîn-nav hat zêdekirin li binbeşa Tewandin ya heyî ==
5l16bksrg68acr5qj3b6zjrhcbnr4gr
6334278
6334272
2026-08-16T23:41:10Z
Balyozbot
7867
[[Special:PermanentLink/6298023|Bot]]: +1 xeta
6334278
wikitext
text/x-wiki
== Nenas beş ==
* [[qilqaldar]]: nenas beş "Jê" (ne hate rêzkirin, ne pos_section e lê binbeş hene)
== Astengî di rêzkirinê de ==
* [[bariyer]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[bayist]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[biyang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[biyokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[biyoteknolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[botanîk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêemrî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêfermanî]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[bêjename]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bêrîvan]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[bûk]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[cilxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[derbûn]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[derkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[dualîtî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[gir]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[gizing]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[hegemonî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[heste]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[heywannasî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[hezîran]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[hiyeroglîf]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[jêkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[kanûna paşîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[kefalet]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[komparatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[kronolojî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[melû]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[meriçîn]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[mexel]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[neqişandin]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nivistinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[nîlûfer]]: astengî di rêzkirinê de ji ber binbeşa "Bi alfabeyên din" (ne hate rêzkirin)
* [[nîsanok]]: astengî di rêzkirinê de ji ber binbeşa "Navê zanistî" (ne hate rêzkirin)
* [[peryod]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[petrokîmya]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[peyîz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[pifdank]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pismam]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[pol]]: astengî di rêzkirinê de ji ber binbeşa "Binêre herwiha" (ne hate rêzkirin)
* [[qap]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qefa]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[qolanî]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[raber]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[raketinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rapelîn]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[razang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[razanxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[revandin]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rezber]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[rûniştinxane]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[serborî]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[surprîz]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[sûperlatîv]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[tabloîd]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[texe]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[teyr]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[tirp]]: astengî di rêzkirinê de ji ber binbeşa "Werger" (ne hate rêzkirin)
* [[tîre]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[werkirin]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[weşanxane]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[wuşe]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[xwarin]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zang]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[zirz]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[zoolojî]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[zêrandin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[çiriya yekem]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[çîvçîv]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[şepilîn]]: astengî di rêzkirinê de ji ber binbeşa "Tewandin" (ne hate rêzkirin)
* [[şor]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şêl]]: astengî di rêzkirinê de ji ber binbeşa "Bide ber" (ne hate rêzkirin)
* [[şêwr]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paş guhê xwe ve neavêtin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[paşguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[piştguh nekirin]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[şehde]]: astengî di rêzkirinê de ji ber binbeşa "Jê" (ne hate rêzkirin)
* [[nahiye]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[zoolog]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[heywannas]]: astengî di rêzkirinê de ji ber binbeşa "Têkildar" (ne hate rêzkirin)
* [[bêdengahî]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[aya]]: astengî di rêzkirinê de ji ber binbeşa "Hevmane" (ne hate rêzkirin)
* [[bijîşk]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
* [[rolyef]]: astengî di rêzkirinê de ji ber binbeşa "Etîmolojî" (ne hate rêzkirin)
* [[mefreq]]: astengî di rêzkirinê de ji ber binbeşa "Formên alternatîv" (ne hate rêzkirin)
mi04q0bdqhjo096xosvhc1toby1l8m4
Bikarhêner:Wikihez/Gadget-test.css
2
1358394
6334089
6333860
2026-08-16T12:41:11Z
Wikihez
7344
6334089
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a hairline before it marks the boundary. Square shape now comes from
the shared .wiktku-group-tab/.wiktku-section-tab rule above, same as
every other tab. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
border-left: 1px solid var(--wikt-rule);
padding-left: 14px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
sxjlu3wwxz0aet45uhepd4v9iwfhmzr
6334090
6334089
2026-08-16T13:14:12Z
Wikihez
7344
6334090
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a hairline before it marks the boundary. Square shape now comes from
the shared .wiktku-group-tab/.wiktku-section-tab rule above, same as
every other tab. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
border-left: 1px solid var(--wikt-rule);
padding-left: 14px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
qpto89327hzewafwt4kj8rdfw2pxqwx
6334091
6334090
2026-08-16T13:16:28Z
Wikihez
7344
6334091
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a hairline before it marks the boundary. Its top-left corner is
squared off (unlike every other tab's rounded-top shape) because the
divider's border-left runs straight into that corner — rounding it
the same as the rest leaves a stray diagonal notch where the border
meets the curve. The top-right stays rounded, matching the other tabs. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
border-left: 1px solid var(--wikt-rule);
padding-left: 14px;
border-top-left-radius: 0;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
g5ppayupu0dfux7du8cxx6lx2tirn5x
6334092
6334091
2026-08-16T13:18:27Z
Wikihez
7344
6334092
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
s9shzof1lq60yddb0cmeclyvplnjy55
6334120
6334092
2026-08-16T15:37:50Z
Wikihez
7344
6334120
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Dever/Şîve — "1c" design: a small "+ Dever/Şîve" toggle on the
sense, not an always-visible field (see buildSenseBlock in start.js).
Chips sit inline before the toggle; clicking it opens a compact panel
with quick-pick suggestions plus free text below. ── */
#wiktku-start-form .wiktku-dfn-dever-wrap {
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-dever-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-dever-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-dever-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-dever-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-dever-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-dever-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-dever-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
}
#wiktku-start-form .wiktku-dfn-dever-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-dever-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-dever-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-dever-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-dever-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-dever-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-dever-add {
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card-sunken);
border-radius: 2px;
font-size: 13px;
padding: 6px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-dever-add:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
4b7b29b8s4ywck5kkgtjqavfo4dipn0
6334123
6334120
2026-08-16T15:51:02Z
Wikihez
7344
6334123
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write an example/quote. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* The toggle sits between the primary groups and the advanced ones, so it
needs no rule of its own beyond a little breathing room. */
#wiktku-start-form .wiktku-sense-adv-row {
margin-top: 14px;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. ── */
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card-sunken);
border-radius: 2px;
font-size: 13px;
padding: 6px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
borzork2hsqp0tgdsdlsd7p7tbsjw3k
6334124
6334123
2026-08-16T16:24:40Z
Wikihez
7344
6334124
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* The three toggles now sit side by side in a flex row (see
.wiktku-dfn-chipfield-row) — give an open panel enough width to stay
usable rather than shrinking to its column's narrow content width. */
min-width: 260px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card-sunken);
border-radius: 2px;
font-size: 13px;
padding: 6px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
9arqulkj4dp23wfq3ef9se74ys7kkhu
6334132
6334124
2026-08-16T17:14:40Z
Wikihez
7344
6334132
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* The three toggles now sit side by side in a flex row (see
.wiktku-dfn-chipfield-row) — give an open panel enough width to stay
usable rather than shrinking to its column's narrow content width. */
min-width: 260px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card-sunken);
border-radius: 2px;
font-size: 13px;
padding: 6px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
sut0li78lzb8f69cdobt99nn4kyok19
6334151
6334132
2026-08-16T17:27:12Z
Wikihez
7344
6334151
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* The three toggles now sit side by side in a flex row (see
.wiktku-dfn-chipfield-row) — give an open panel enough width to stay
usable rather than shrinking to its column's narrow content width.
max-width is the important half of this pair: without it, nothing
bounds .wiktku-dfn-chipfield-suggest's width, so its flex-wrap never
kicks in — Dever/Şîve's 11 suggestion pills lay out on one unbroken
~700px line instead of wrapping, and the whole panel (and its wrap,
a flex item in the row) grows to fit them, shoving the other two
toggles out of the row. Confirmed via computed layout in a live
browser test, not guessed. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card-sunken);
border-radius: 2px;
font-size: 13px;
padding: 6px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
8gu7jqg2g1urbe9ye4xk73sf75bxlsm
6334171
6334151
2026-08-16T18:00:25Z
Wikihez
7344
6334171
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
gjs1sevzvhyirhxte1f6yhgvad3tgpq
6334190
6334171
2026-08-16T19:04:48Z
Wikihez
7344
6334190
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
p3vc2dul4lsjjwcx6045l40lsfr89dl
6334195
6334190
2026-08-16T19:14:25Z
Wikihez
7344
6334195
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
4rzy6jmzfqv7earx7e31oqkvdc39fms
6334197
6334195
2026-08-16T19:24:27Z
Wikihez
7344
6334197
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
75kjyidr003p5mfxolvpjfkyypwxeyp
6334204
6334197
2026-08-16T19:54:17Z
Wikihez
7344
6334204
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — a small
hover/focus tooltip via the native title attribute. ── */
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: help;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
acii6sdd7uov09aqh6470oey59gn6tv
6334212
6334204
2026-08-16T20:22:05Z
Wikihez
7344
6334212
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results,
html.skin-theme-clientpref-night .wiktku-field-help-popover {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results,
html.skin-theme-clientpref-os .wiktku-field-help-popover {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ── "?" field-help icon (see makeFieldHelp in start.js) — click/tap opens
a small popover next to it; works on touch, unlike a title attribute. ── */
#wiktku-start-form .wiktku-field-help-wrap {
position: relative;
display: inline-block;
flex: 0 0 auto;
vertical-align: middle;
}
#wiktku-start-form .wiktku-field-help {
display: inline-grid;
place-items: center;
width: 17px;
height: 17px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-muted);
font-size: 10.5px;
cursor: pointer;
flex: 0 0 auto;
vertical-align: middle;
margin-left: 2px;
}
#wiktku-start-form .wiktku-field-help:hover,
#wiktku-start-form .wiktku-field-help:focus,
#wiktku-start-form .wiktku-field-help[aria-expanded="true"] {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-field-help-popover {
position: absolute;
z-index: 20;
top: calc(100% + 6px);
left: 0;
width: max-content;
min-width: 180px;
max-width: min(260px, 80vw);
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
padding: 8px 10px;
font-size: 12.5px;
line-height: 1.4;
color: var(--wikt-ink);
white-space: normal;
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Only used when the step also has a "?" tooltip (see step.help in
start.js) — the question label's own margin-bottom still applies, this
just keeps the two on one line instead of the help icon falling to a
line of its own below the (block-level) question label. */
#wiktku-start-form .wiktku-start-step-question-row {
display: flex;
align-items: baseline;
gap: 6px;
margin: 0 0 12px;
}
#wiktku-start-form .wiktku-start-step-question-row .wiktku-start-step-question {
margin-bottom: 0;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
pbjzscb0txd1vx6u2cjv9yhllsrqwxn
6334220
6334212
2026-08-16T20:39:24Z
Wikihez
7344
6334220
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
5c61vzv4pu2p87x8heo3vg0asoeqqzz
6334230
6334220
2026-08-16T20:55:40Z
Wikihez
7344
6334230
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it is an alternative route, not the first control in the sense.
Hidden behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-top: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-top: 10px;
padding-top: 12px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
53rynj5b5iedzmj86lbtberb4eugomp
6334233
6334230
2026-08-16T21:00:56Z
Wikihez
7344
6334233
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
75c10lmn6d52fbqdmbn2dqcgnq94wpl
6334238
6334233
2026-08-16T21:20:39Z
Wikihez
7344
6334238
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.wiktku-dfn-quote-src-fields {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
align-items: baseline;
gap: 4px;
flex-wrap: wrap;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
min-width: 8em;
}
textarea.wiktku-dfn-quote-src-input {
flex: 1 1 12em;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
.wiktku-dfn-quote-src-row label {
min-width: 6em;
}
#wiktku-start-form textarea.wiktku-dfn-quote-src-input {
width: 100%;
flex: 1 1 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
/* The persistent pair next to the template picker (as opposed to a
dropdown row) sits inline beside the search box rather than as a
flex child of a list row. */
#wiktku-start-form .wiktku-dfn-tpl-sel-icons {
display: inline-flex;
vertical-align: middle;
margin-left: 6px;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
cabhr8apx8q8ygyj2bpzjg5veasms0o
6334275
6334238
2026-08-16T23:36:26Z
Wikihez
7344
6334275
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
/* ── ⊕-modifier word lists (Hevmane/Dijmane/Formên alternatîv/Termên
koordîne/Jornav/Jêrnav — both the top-level steps and the per-sense
sub-groups): committed words collapse into pills that wrap in a row,
while the still-open "type here" row spans the full line. See
makeModifierLi's collapse()/expand() in start.js. ── */
#wiktku-start-form .wiktku-word-list--pills,
#wiktku-start-form ol.wiktku-dfn-sublist.wiktku-dfn-sublist--pills {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px;
list-style: none;
margin: 0;
padding: 0;
}
#wiktku-start-form .wiktku-word-list--pills > li,
#wiktku-start-form ol.wiktku-dfn-sublist--pills > li {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-word-list--pills > li.wiktku-word-li:not(.wiktku-word-li--pilled),
#wiktku-start-form ol.wiktku-dfn-sublist--pills > li.wiktku-word-li:not(.wiktku-word-li--pilled) {
flex: 1 1 100%;
}
#wiktku-start-form .wiktku-word-li--pilled {
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-word-pill {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 5px 6px 5px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-pill-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 22em;
}
#wiktku-start-form .wiktku-word-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-word-pill-remove:hover {
color: var(--wikt-danger);
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Wide/narrow/medium layout, same idea as MediaWiki:Gadget-wikt.add-
examples.js: rows wrap freely, each sized by its own width tier
(.wiktku-dfn-quote-src-row--wide/narrow/medium) instead of always
stacking one field per line. */
.wiktku-dfn-quote-src-fields {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px 10px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row--wide {
flex-basis: 100%;
}
.wiktku-dfn-quote-src-row--narrow {
flex: 0 1 7em;
}
.wiktku-dfn-quote-src-row--medium {
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
}
.wiktku-dfn-quote-src-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
/* Every field stacks full-width on mobile, regardless of its wide/
narrow/medium tier — there's no room to wrap several per line. */
#wiktku-start-form .wiktku-dfn-quote-src-row {
flex-basis: 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
amq6sprtpv9dw4djo1eh9p6j60x2j9u
6334289
6334275
2026-08-17T08:19:28Z
Wikihez
7344
6334289
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
.wiktku-word-li {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
gap: 0.3em;
}
/* ── ⊕-modifier word lists (Hevmane/Dijmane/Formên alternatîv/Termên
koordîne/Jornav/Jêrnav — both the top-level steps and the per-sense
sub-groups): committed words collapse into pills that wrap in a row,
while the still-open "type here" row spans the full line. See
makeModifierLi's collapse()/expand() in start.js. ── */
#wiktku-start-form .wiktku-word-list--pills,
#wiktku-start-form ol.wiktku-dfn-sublist.wiktku-dfn-sublist--pills {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px;
list-style: none;
margin: 0;
padding: 0;
}
#wiktku-start-form .wiktku-word-list--pills > li,
#wiktku-start-form ol.wiktku-dfn-sublist--pills > li {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-word-list--pills > li.wiktku-word-li:not(.wiktku-word-li--pilled),
#wiktku-start-form ol.wiktku-dfn-sublist--pills > li.wiktku-word-li:not(.wiktku-word-li--pilled) {
flex: 1 1 100%;
}
#wiktku-start-form .wiktku-word-li--pilled {
flex: 0 0 auto;
}
/* A word with modifier chips always spans the full row — its chip line is
usually wider than the pill itself, so packing it beside another word's
pill would read as attached to both instead of clearly just this one. */
#wiktku-start-form .wiktku-word-list--pills > li.wiktku-word-li--has-mods,
#wiktku-start-form ol.wiktku-dfn-sublist--pills > li.wiktku-word-li--has-mods {
flex-basis: 100%;
}
#wiktku-start-form .wiktku-word-pill {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 5px 6px 5px 12px;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-pill-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 22em;
}
#wiktku-start-form .wiktku-word-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-word-pill-remove:hover {
color: var(--wikt-danger);
}
/* The word box (or pill, once collapsed) sits on the li's first line; a
modifier chip row — one small chip per attached ⊕ modifier — sits below
it. flex-basis:100% forces it onto its own line in .wiktku-word-li's
flex-wrap row regardless of how much room the word/pill itself took. */
#wiktku-start-form .wiktku-word-mods {
flex-basis: 100%;
display: flex;
flex-wrap: wrap;
gap: 4px;
}
#wiktku-start-form .wiktku-word-mod-chip {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--wikt-card);
color: var(--wikt-ink-2);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
font-size: 11.5px;
padding: 2px 4px 2px 8px;
}
#wiktku-start-form .wiktku-word-mod-chip-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 12px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-word-mod-chip-remove:hover {
color: var(--wikt-danger);
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Wide/narrow/medium layout, same idea as MediaWiki:Gadget-wikt.add-
examples.js: rows wrap freely, each sized by its own width tier
(.wiktku-dfn-quote-src-row--wide/narrow/medium) instead of always
stacking one field per line. */
.wiktku-dfn-quote-src-fields {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px 10px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row--wide {
flex-basis: 100%;
}
.wiktku-dfn-quote-src-row--narrow {
flex: 0 1 7em;
}
.wiktku-dfn-quote-src-row--medium {
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
}
.wiktku-dfn-quote-src-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
/* Every field stacks full-width on mobile, regardless of its wide/
narrow/medium tier — there's no room to wrap several per line. */
#wiktku-start-form .wiktku-dfn-quote-src-row {
flex-basis: 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-word-input {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
mws1tj27rumzwrsdwk5ihy87uo0izd9
6334291
6334289
2026-08-17T08:39:16Z
Wikihez
7344
6334291
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-word-input {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus,
#wiktku-start-form textarea.wiktku-word-input:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
/* Each word is always a plain box (never collapses into a pill shape); its
attached-modifier chips, if any, sit on their own line right above the
box, and the box + its ⊕/× buttons sit on their own line below that. */
.wiktku-word-li {
display: block;
}
/* ── ⊕-modifier word lists (Hevmane/Dijmane/Formên alternatîv/Termên
koordîne/Jornav/Jêrnav — both the top-level steps and the per-sense
sub-groups): every word is its own always-visible, always-editable box,
one per line — no packing/wrapping of multiple words onto one row. ── */
#wiktku-start-form .wiktku-dfn-sublist > li.wiktku-word-li {
margin-bottom: 6px;
}
/* Modifier chip row: sits above the word's box (DOM order in start.js
already places it first; this just spaces it out from the box below). */
#wiktku-start-form .wiktku-word-mods {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 3px;
}
#wiktku-start-form .wiktku-word-mod-chip {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--wikt-card);
color: var(--wikt-ink-2);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
font-size: 11.5px;
padding: 2px 4px 2px 8px;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-mod-chip-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 12px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-word-mod-chip-remove:hover {
color: var(--wikt-danger);
}
/* Word box + its ⊕ (add modifier) / × (remove word) buttons, side by side. */
#wiktku-start-form .wiktku-word-box-row {
display: flex;
align-items: center;
gap: 4px;
}
#wiktku-start-form .wiktku-word-box-row .wiktku-word-input {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-word-remove {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-muted);
font-size: 1em;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-remove:hover {
border-color: var(--wikt-danger);
color: var(--wikt-danger);
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. */
ul.wiktku-ac-results {
position: absolute;
z-index: 9999;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Wide/narrow/medium layout, same idea as MediaWiki:Gadget-wikt.add-
examples.js: rows wrap freely, each sized by its own width tier
(.wiktku-dfn-quote-src-row--wide/narrow/medium) instead of always
stacking one field per line. */
.wiktku-dfn-quote-src-fields {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px 10px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row--wide {
flex-basis: 100%;
}
.wiktku-dfn-quote-src-row--narrow {
flex: 0 1 7em;
}
.wiktku-dfn-quote-src-row--medium {
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
}
.wiktku-dfn-quote-src-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
/* Every field stacks full-width on mobile, regardless of its wide/
narrow/medium tier — there's no room to wrap several per line. */
#wiktku-start-form .wiktku-dfn-quote-src-row {
flex-basis: 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-word-input {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
ju4q74x05a2kum4epf9mcu14sighxu1
6334302
6334291
2026-08-17T11:32:17Z
Wikihez
7344
6334302
css
text/css
/* ===================================================================
#editform while the wizard is active
=================================================================== */
/* Keeps #editform genuinely laid out instead of display:none*/
.wiktku-offscreen {
position: absolute !important;
left: -9999px !important;
top: 0 !important;
width: 100%;
max-width: 100%;
visibility: hidden;
}
/* ===================================================================
Base scale
=================================================================== */
#wiktku-start-form {
font-size: 0.92em;
line-height: 1.35;
}
#wiktku-start-form * {
box-sizing: border-box;
}
.wiktku-addlang-notice {
margin: 0 0 0.8em;
padding: 0.6em 0.8em;
background: var(--wikt-palette-paleblue);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
}
/* ===================================================================
Step cards
a compact card with a left accent bar, so each step reads as
a distinct unit
=================================================================== */
.wiktku-start-step {
padding: 0.7em 0.9em;
margin-bottom: 0.6em;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-left: 4px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
transition: border-left-color 0.15s ease, box-shadow 0.15s ease;
}
.wiktku-start-step:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.wiktku-start-step:focus-within {
border-left-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
/* Fuses the posparams card onto the bottom of the pos card (shared left
accent bar, no border/gap) since posparams always renders directly
after #wiktku-start-pos once a pos is chosen. */
#wiktku-start-pos {
border-radius: 6px 6px 0 0;
margin-bottom: 0;
border-bottom: none;
}
#wiktku-start-posparams {
border-radius: 0 0 6px 6px;
border-top: none;
padding-top: 0.3em;
}
#wiktku-start-posparams .wiktku-start-step-name {
display: none;
}
/* Scoped and !important: this is a real <h3> tag (see start.js), so the
skin's default heading styles would otherwise override a plain class
rule. Coloured to match the card's accent bar; .wiktku-start-step-question
below stays lighter/muted so the two don't compete. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.1em !important;
font-weight: 700;
letter-spacing: 0.03em;
margin: 0 0 0.3em;
line-height: 1.2;
color: var(--wikt-palette-blue-9, #3366cc);
border: 0;
padding: 0;
}
/* Sized close to the heading so the card reads consistently instead of
the text below it looking like fine print. */
.wiktku-start-step-question {
font-size: 1em;
font-weight: 400;
color: var(--wikt-palette-grey-9, #555);
margin: 0 0 0.5em;
line-height: 1.45;
}
.wiktku-start-step label {
padding-right: 1.2em;
font-size: 1em;
}
/* Muted hint text next to a field, styled to read as a hint rather than
another question competing with .wiktku-start-step-question. */
.wiktku-step-hint {
font-size: 0.88em;
font-style: italic;
color: var(--wikt-palette-grey-8, #72777d);
margin: 0 0 0.5em;
line-height: 1.4;
}
/* Same specificity as the rule just above, so it must come after to
actually win: the name label isn't a field label needing right
padding or the form's base label size, it's a heading. */
#wiktku-start-form .wiktku-start-step-name label {
padding-right: 0;
font-size: inherit !important;
color: inherit;
}
input[type="text"].wiktku-start-step-field {
width: 100%;
padding: 5px 8px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 4px;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input[type="text"].wiktku-start-step-field:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
input[type="text"].wiktku-start-step-field.wiktku-start-narrow {
width: 20em;
}
/* Only used by selects that opt out via .wiktku-plain-select; other
selects get their width from the searchable picker instead (see
input.wiktku-ac-input). */
select.wiktku-start-narrow,
input.wiktku-dfn-tpl-title {
width: 20em;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-spinner {
float: right;
margin-left: 1em;
}
#wiktku-start-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5em;
padding: 0.6em 0.2em;
position: sticky;
bottom: 0;
background: var(--wikt-palette-white);
border-top: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
z-index: 5;
}
/* Quiet, non-competing text — sits next to the cache buttons so the
person can tell at a glance whether their latest edits are backed
up, without it fighting the buttons for attention. */
#wiktku-last-saved {
font-size: 0.85em;
color: var(--wikt-palette-grey-9, #767676);
white-space: nowrap;
}
/* ── Tercîh (settings) icon button — sits in the footer next to the
cache buttons. Uses the Codex "settings" (color-subtle) icon from
Commons directly rather than an OOUI icon-pack module, so it renders
the exact requested glyph without pulling in an extra dependency. */
.wiktku-prefs-btn {
width: 2.2em;
height: 2.2em;
flex: 0 0 auto;
padding: 0;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 2px;
background-color: var(--wikt-palette-white);
background-image: url(//upload.wikimedia.org/wikipedia/commons/e/e0/Codex_icon_settings_color-subtle.svg);
background-repeat: no-repeat;
background-position: center;
background-size: 1.2em 1.2em;
cursor: pointer;
}
.wiktku-prefs-btn:hover {
background-color: var(--wikt-palette-grey-0, #f8f9fa);
}
.wiktku-prefs-btn:active {
background-color: var(--wikt-palette-grey-1, #eaecf0);
}
/* Tercîh dialog body — OOUI's bare ProcessDialog body has no inset of
its own, so text/fields otherwise sit flush against the window edges. */
.wiktku-prefs-dialog-body {
padding: 1em 1.2em;
}
/* ── "Bişîne" click feedback ── */
@keyframes wiktku-btn-flash-pulse {
0% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0.55); }
70% { box-shadow: 0 0 0 9px rgba(51, 102, 204, 0); }
100% { box-shadow: 0 0 0 0 rgba(51, 102, 204, 0); }
}
#wiktku-unhide-editform.wiktku-btn-flash {
animation: wiktku-btn-flash-pulse 0.5s ease-out;
}
/* Persistent state while the wikitext is being built/submitted, set
alongside OOUI's own setDisabled(true) so it reads clearly even if
the skin's default disabled style is subtle. */
#wiktku-unhide-editform.wiktku-btn-sending .oo-ui-buttonElement-button {
background-color: var(--wikt-palette-grey-9, #767676) !important;
border-color: var(--wikt-palette-grey-9, #767676) !important;
cursor: progress;
opacity: 1 !important;
}
/* Looks like OOUI's disabled state but isn't one — the button stays
clickable so its handler can still run and explain why (see the
wiktkuNotify call next to wiktkuContentGateOk in start.js). */
#wiktku-unhide-editform.wiktku-btn-soft-disabled .oo-ui-buttonElement-button {
opacity: 0.5;
cursor: not-allowed;
}
/* ── "Beşên din" picker ── */
.wiktku-besendin-picker {
margin: 0.6em 0 1em;
}
.wiktku-besendin-picker .wiktku-besendin-select {
width: 100%;
max-width: 24em;
}
#wiktku-start-form textarea.wiktku-start-step-field,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-word-input {
resize: vertical !important;
overflow: auto !important;
box-sizing: border-box !important;
vertical-align: top;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
/* Modern focus state to match the searchable-picker inputs. */
#wiktku-start-form textarea.wiktku-start-step-field:focus,
#wiktku-start-form textarea.wiktku-start-narrow:focus,
#wiktku-start-form textarea.wiktku-dfn-subfield:focus,
#wiktku-start-form textarea.wiktku-word-input:focus {
border-color: var(--wikt-palette-blue-9, #3366cc);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9, #3366cc);
outline: 0;
}
/* Standard: every textarea in the form is full width by default */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 20em;
max-width: 100%;
min-width: 8em;
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px !important;
padding-bottom: 4px !important;
line-height: 1.35 !important;
resize: none !important;
}
/* Exception: "Sernav" / "Sernavê orjînal" citation-source fields (kitêb,
kovar, malper, …) — long enough to be worth 2 rows and manual resizing. */
#wiktku-start-form textarea.wiktku-dfn-quote-src-input.wiktku-dfn-quote-src-input--title {
height: calc(2 * 1.35em + 10px);
min-height: calc(2 * 1.35em + 10px);
}
/* ── Manual drag-to-resize handle ── */
#wiktku-start-form textarea:not(.wiktku-start-narrow) {
resize: none !important;
}
/* Inline modifier button (⊕) */
/* Each word is always a plain box (never collapses into a pill shape); its
attached-modifier chips, if any, sit on their own line right above the
box, and the box + its ⊕/× buttons sit on their own line below that. */
.wiktku-word-li {
display: block;
}
/* ── ⊕-modifier word lists (Hevmane/Dijmane/Formên alternatîv/Termên
koordîne/Jornav/Jêrnav — both the top-level steps and the per-sense
sub-groups): every word is its own always-visible, always-editable box,
one per line — no packing/wrapping of multiple words onto one row. ── */
#wiktku-start-form .wiktku-dfn-sublist > li.wiktku-word-li {
margin-bottom: 6px;
}
/* Modifier chip row: sits above the word's box (DOM order in start.js
already places it first; this just spaces it out from the box below). */
#wiktku-start-form .wiktku-word-mods {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 3px;
}
#wiktku-start-form .wiktku-word-mod-chip {
display: inline-flex;
align-items: center;
gap: 4px;
background: var(--wikt-card);
color: var(--wikt-ink-2);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
font-size: 11.5px;
padding: 2px 4px 2px 8px;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-mod-chip-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 12px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-word-mod-chip-remove:hover {
color: var(--wikt-danger);
}
/* Word box + its ⊕ (add modifier) / × (remove word) buttons, side by side. */
#wiktku-start-form .wiktku-word-box-row {
display: flex;
align-items: center;
gap: 4px;
}
#wiktku-start-form .wiktku-word-box-row .wiktku-word-input {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-word-remove {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-muted);
font-size: 1em;
cursor: pointer;
}
#wiktku-start-form .wiktku-word-remove:hover {
border-color: var(--wikt-danger);
color: var(--wikt-danger);
}
.wiktku-mod-btn {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: var(--wikt-palette-blue-9, #3366cc);
font-size: 1em;
cursor: pointer;
}
.wiktku-mod-btn:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-mod-picker {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
padding: 0.35em;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
background: var(--wikt-palette-white);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.wiktku-mod-pill {
padding: 2px 8px;
border: 1px solid var(--wikt-palette-grey-3, #e0e0e0);
border-radius: 12px;
background: var(--wikt-palette-paleblue, #eaf3ff);
font-size: 0.85em;
white-space: nowrap;
cursor: pointer;
}
.wiktku-mod-pill:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
}
.wiktku-mod-pill:disabled,
.wiktku-mod-pill.wiktku-mod-pill-used {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
cursor: not-allowed;
opacity: 0.6;
}
.wiktku-mod-pill:disabled:hover,
.wiktku-mod-pill.wiktku-mod-pill-used:hover {
border-color: var(--wikt-palette-grey-3, #e0e0e0);
background: var(--wikt-palette-lightgrey);
color: var(--wikt-palette-grey-3, #999);
}
.wiktku-mod-val-wrap {
display: flex;
align-items: center;
gap: 0.25em;
width: 100%;
margin-top: 0.15em;
}
.wiktku-mod-val-input {
flex: 0 1 14em;
max-width: 14em;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
}
.wiktku-mod-confirm,
.wiktku-mod-cancel {
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
padding: 0;
line-height: 1;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
cursor: pointer;
}
.wiktku-mod-confirm {
color: #178a17;
border-color: #178a17;
}
.wiktku-mod-cancel {
color: var(--wikt-palette-red-9, #d33);
border-color: var(--wikt-palette-red-9, #d33);
}
.wiktku-resize-wrap {
position: relative;
display: block;
}
/* Separate from .wiktku-resize-wrap (used only for textareas flagged with
a linebreak); plain position:relative anchor for the backdrop, no size of its own. */
.wiktku-linebreak-wrap {
position: relative;
display: block;
}
.wiktku-resize-grip {
position: absolute;
right: 2px;
bottom: 2px;
width: 14px;
height: 14px;
cursor: ns-resize;
touch-action: none;
opacity: 0.5;
z-index: 2;
background-image: linear-gradient(135deg, transparent 0 50%, var(--wikt-palette-grey-9, #767676) 50% 100%);
background-repeat: no-repeat;
}
.wiktku-resize-grip:hover,
.wiktku-resize-grip:active {
opacity: 0.9;
}
/* ===================================================================
Searchable dropdown (wrapNativeSelect in start.js)
.wiktku-native-select-wrap > input.wiktku-ac-input (search box)
> ul.wiktku-ac-results (result list)
> select (hidden, holds the value)
=================================================================== */
.wiktku-native-select-wrap {
display: inline-block;
position: relative;
vertical-align: top;
}
/* Matches the width the native select would have had (see
select.wiktku-start-narrow). */
.wiktku-native-select-wrap input[type="search"],
input.wiktku-ac-input {
display: block;
width: 20em;
max-width: 100%;
box-sizing: border-box;
padding: 3px 6px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 2px;
background: var(--wikt-palette-white);
color: inherit;
font-size: inherit;
font-family: inherit;
transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
input.wiktku-ac-input:focus {
border-color: var(--wikt-palette-blue-9);
box-shadow: inset 0 0 0 1px var(--wikt-palette-blue-9);
outline: 0;
}
input.wiktku-ac-input::placeholder {
color: var(--wikt-palette-grey-9);
opacity: 1;
}
/* Hide Safari/Chrome's built-in search decorations. */
input.wiktku-ac-input::-webkit-search-decoration,
input.wiktku-ac-input::-webkit-search-cancel-button,
input.wiktku-ac-input::-webkit-search-results-button {
-webkit-appearance: none;
appearance: none;
}
/* ── Result list ── */
/* top/bottom/max-height are overridden inline by showResults() when it
flips the list above the box; these are just its defaults. Kept well
below MediaWiki's own .mw-notification-area (z-index: 10000) so an
open dropdown never renders on top of a toast notification — every
other z-index in this form tops out at 70, so 500 is still plenty to
clear all of them. */
ul.wiktku-ac-results {
position: absolute;
z-index: 500;
top: 100%;
bottom: auto;
left: 0;
margin: 0;
padding: 0;
list-style: none;
width: 20em;
max-width: 90vw;
max-height: 13em;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
background: var(--wikt-palette-white);
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 3px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
}
ul.wiktku-ac-results li {
margin: 0;
padding: 4px 8px;
cursor: pointer;
font-size: 0.92em;
display: flex;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .wiktku-ac-active is the keyboard cursor; hover mirrors it; :active
covers the touch press state. */
ul.wiktku-ac-results li.wiktku-ac-active,
ul.wiktku-ac-results li:hover {
background: var(--wikt-palette-lightblue);
}
ul.wiktku-ac-results li:active {
background: var(--wikt-palette-blue-9);
color: var(--wikt-palette-white);
}
/* Touch devices: bigger tap targets, no sticky hover. */
@media (pointer: coarse) {
ul.wiktku-ac-results li {
padding: 6px 10px;
min-height: 2.2em;
font-size: 1em;
}
ul.wiktku-ac-results li:hover {
background: transparent;
}
ul.wiktku-ac-results li.wiktku-ac-active {
background: var(--wikt-palette-lightblue);
}
input.wiktku-ac-input {
/* >=16px stops iOS Safari zooming the page when the box is focused. */
font-size: 16px;
min-height: 2.2em;
}
}
/* Inline preview + confirm panel shown before inserting an auto-content
pronunciation template, part of the form layout rather than a browser
confirm()/prompt() dialog. */
.wiktku-pron-preview {
margin: 6px 0;
padding: 8px 10px;
border: 1px solid var(--wikt-palette-blue-9, #3366cc);
border-radius: 6px;
background: var(--wikt-palette-paleblue, #eaf3ff);
}
.wiktku-pron-preview-label {
font-size: 0.88em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #555);
margin-bottom: 3px;
}
.wiktku-pron-preview-body {
font-family: monospace;
font-size: 1em;
white-space: pre-wrap;
word-break: break-word;
margin-bottom: 6px;
}
.wiktku-pron-preview-actions {
display: flex;
gap: 6px;
}
.wiktku-pron-preview-confirm,
.wiktku-pron-preview-cancel {
font-size: 0.88em;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
border: 1px solid var(--wikt-palette-lightgrey, #a2a9b1);
background: var(--wikt-palette-white, #fff);
}
.wiktku-pron-preview-confirm {
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-blue-9, #3366cc);
font-weight: 600;
}
.wiktku-pron-preview-confirm:disabled {
opacity: 0.5;
cursor: wait;
}
.wiktku-pron-preview-cancel:hover {
background: var(--wikt-palette-grey-0, #f8f9fa);
}
/* Template pickers fill their container since the search box, not the
<select>, is what the user sees. */
.wiktku-pron-wrap .wiktku-native-select-wrap,
.wiktku-etym-wrap .wiktku-native-select-wrap,
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
.wiktku-cavkani-wrap .wiktku-native-select-wrap,
.wiktku-cavkani-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
width: 100%;
}
/* Template selector inside a sense block. */
.wiktku-dfn-tpl-row .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 0.88em;
}
/* Language/dialect rows: picker must line up with the word field beside it. */
.wiktku-lang-row .wiktku-native-select-wrap {
flex: 0 0 auto;
}
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual,
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text,
#wiktku-start-form textarea.wiktku-dfn-tercime,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 2;
height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
min-height: calc(var(--wiktku-ta-rows) * 1.35em + 6px);
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text,
#wiktku-start-form textarea.wiktku-dfn-izah {
--wiktku-ta-rows: 3;
}
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
}
/* Mane, Nimûne + Jêgirtin (citation) boxes, and their tr/t sub-fields: at
least 4 rows so there's room to actually write a definition/example/quote.
Manual resize (see util.js) still lets the person grow or shrink any of
these afterward — this only sets where they start. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t,
#wiktku-start-form textarea.wiktku-dfn-quote-src-manual {
--wiktku-ta-rows: 4;
}
/* Nudges the border when the box contains a typed/pasted linebreak, so
the user notices before it's silently turned into <br> on output; the
break itself is marked via a mirrored backdrop layer (see below). */
#wiktku-start-form textarea.wiktku-has-linebreak {
border-color: var(--wikt-palette-yellow6, #ac6600) !important;
}
/* Sits behind the textarea showing the same text, with a highlighted
marker at each line followed by a break; font/padding/line-height are
copied from the textarea via computed style, not shared CSS classes. */
.wiktku-linebreak-backdrop {
position: absolute;
inset: 0;
z-index: 0;
margin: 0;
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
resize: none !important;
pointer-events: none;
}
/* Raise the real textarea above its backdrop so typing/selection/caret
behave completely normally; the backdrop's mark shows through only
where the real textarea's own background is transparent. */
#wiktku-start-form textarea.wiktku-has-linebreak-source {
position: relative;
z-index: 1;
background-color: transparent;
}
.wiktku-linebreak-mark {
background-color: var(--wikt-palette-yellow6, #ac6600);
color: var(--wikt-palette-white, #fff);
border-radius: 2px;
}
/* ── Tercime + Îzah boxes ── */
.wiktku-dfn-nonku-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-top: 3px;
margin-left: 1em;
padding-left: 0.6em;
border-left: 2px solid var(--wikt-palette-lightgrey);
display: flex;
flex-direction: column;
gap: 3px;
}
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
.wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t {
min-width: 8em;
padding-top: 2px !important;
padding-bottom: 2px !important;
line-height: 1.35 !important;
font-size: 0.88em;
}
/* Sense list (Mane step) — one sense visible at a time, switched via
.wiktku-dfn-sense-tabs. */
ol.wiktku-dfn-list {
list-style: none;
margin: 0;
padding: 0;
}
ol.wiktku-dfn-list > li.wiktku-dfn-sense {
margin-bottom: 0.9em;
}
/* Template selector row inside a sense block */
.wiktku-dfn-tpl-row {
margin-bottom: 3px;
}
/* ── Sub-field titles (Etîket, Kategorî, Hevmane, Dijmane, Nimûne,
Jêgirtin, …): each group reads as a small labelled field — the term
stands out, the hint stays quiet. ── */
.wiktku-dfn-subgroup {
margin-top: 0.5em;
padding-top: 0.4em;
border-top: 1px dashed var(--wikt-palette-grey-3, #e0e0e0);
}
.wiktku-dfn-subgroup:first-of-type {
margin-top: 0.3em;
padding-top: 0;
border-top: none;
}
.wiktku-dfn-sublabel {
display: block;
margin-bottom: 3px;
font-size: 0.88em;
}
.wiktku-dfn-sublabel-term {
font-weight: 600;
color: var(--wikt-palette-blue-9, #3366cc);
letter-spacing: 0.03em;
font-size: 0.92em;
}
.wiktku-dfn-sublabel-hint {
color: var(--wikt-palette-grey-9, #767676);
font-style: italic;
}
.wiktku-pron-wrap,
.wiktku-etym-wrap,
.wiktku-tewandin-wrap,
.wiktku-cavkani-wrap {
display: flex;
flex-direction: column;
gap: 3px;
}
/* ── POS section tabs — rounded pill style ── */
#wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.6em;
align-items: center;
}
#wiktku-section-tabs .wiktku-section-tab {
padding: 3px 12px;
border: 1px solid var(--wikt-palette-lightgrey);
border-radius: 999px;
background: var(--wikt-palette-dullcyan);
cursor: pointer;
font-size: 0.86em;
user-select: none;
transition: background 0.12s ease, border-color 0.12s ease;
}
#wiktku-section-tabs .wiktku-section-tab:hover {
border-color: var(--wikt-palette-blue-9, #3366cc);
}
#wiktku-section-tabs .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-palette-blue-9, #3366cc);
border-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
font-weight: 600;
}
/* ── Step-group tabs (Mane / Bilêvkirin / Beşên din — see STEP_GROUPS in
start.js) ── Sits directly under the pill-style section tabs above;
styled as an underlined secondary nav so the two bars stay visually
distinct. */
#wiktku-group-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-bottom: 0.8em;
border-bottom: 2px solid var(--wikt-palette-lightgrey);
}
#wiktku-group-tabs .wiktku-group-tab {
position: relative;
padding: 7px 14px 8px;
margin-bottom: -2px;
border-bottom: 3px solid transparent;
border-radius: 3px 3px 0 0;
cursor: pointer;
font-size: 0.95em;
font-weight: 600;
color: var(--wikt-palette-grey-9, #333);
background: var(--wikt-palette-grey-0, #f8f9fa);
user-select: none;
transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#wiktku-group-tabs .wiktku-group-tab:hover {
color: var(--wikt-palette-blue-9, #3366cc);
background: var(--wikt-palette-paleblue, #eaf3ff);
}
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-active {
border-bottom-color: var(--wikt-palette-blue-9, #3366cc);
color: var(--wikt-palette-white);
background: var(--wikt-palette-blue-9, #3366cc);
font-weight: 700;
}
/* Small dot marking a tab that still has an unfilled required field
hidden inside it (see updateGroupTabIndicators in start.js). Cleared
automatically once that tab becomes the active one. */
#wiktku-group-tabs .wiktku-group-tab.wiktku-tab-missing::after {
content: "";
position: absolute;
top: 4px;
right: -1px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--wikt-palette-red-9, #d33);
}
.wiktku-lang-row-list {
margin: 0;
padding: 0;
list-style: none;
}
.wiktku-lang-row {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 3px;
flex-wrap: wrap;
}
.wiktku-lang-row-words {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 3px;
}
/* Dûnde/Werger/Zarava rows: align the picker's height with the adjacent
word textarea (see .wiktku-start-narrow above). */
.wiktku-lang-row .wiktku-native-select-wrap input.wiktku-ac-input {
height: calc(1.7em + 10px);
min-height: calc(1.7em + 10px);
padding-top: 4px;
padding-bottom: 4px;
}
/* ── Werger / Bi zaravayên din groups: one per gloss, shared spacing ── */
#wiktku-start-trans .wiktku-trans-group,
#wiktku-start-zarava .wiktku-zarava-group {
margin-bottom: 0.6em;
}
#wiktku-start-trans .wiktku-trans-group:last-child,
#wiktku-start-zarava .wiktku-zarava-group:last-child {
margin-bottom: 0;
}
#wiktku-start-trans .wiktku-trans-group-label,
#wiktku-start-zarava .wiktku-zarava-group-label {
font-weight: 600;
font-size: 0.88em;
margin-bottom: 3px;
}
.wiktku-dfn-jeder-group {
margin-top: 3px;
}
.wiktku-dfn-quote-src-sel {
font-size: 0.88em;
margin-top: 3px;
display: block;
/* A native select with no width rule sizes to fit its longest <option>
(here, the longest çavkanî template name) rather than its container,
which overflows the card on a phone — cap it like every other field. */
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Wide/narrow/medium layout, same idea as MediaWiki:Gadget-wikt.add-
examples.js: rows wrap freely, each sized by its own width tier
(.wiktku-dfn-quote-src-row--wide/narrow/medium) instead of always
stacking one field per line. */
.wiktku-dfn-quote-src-fields {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 6px 10px;
margin-top: 3px;
}
/* Stacked layout: textarea gets its own full-width row, button + status
sit below it on their own row. */
.wiktku-dfn-quote-citoid {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4em;
margin-top: 3px;
margin-bottom: 3px;
}
.wiktku-dfn-quote-citoid-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* Column flex + align-items:stretch (above) makes the textarea's
.wiktku-resize-wrap reach full width automatically; button and status
text opt back out via align-self so they keep their natural size. */
.wiktku-dfn-quote-citoid-btn,
.wiktku-dfn-quote-citoid-status {
align-self: flex-start;
}
.wiktku-dfn-quote-src-cat-status {
display: block;
font-size: 0.82em;
color: var(--wikt-palette-lightgrey, #767676);
margin-top: 2px;
}
.wiktku-dfn-quote-src-row {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row--wide {
flex-basis: 100%;
}
.wiktku-dfn-quote-src-row--narrow {
flex: 0 1 7em;
}
.wiktku-dfn-quote-src-row--medium {
flex: 1 1 12em;
}
.wiktku-dfn-quote-src-row label {
font-size: 0.82em;
white-space: nowrap;
}
.wiktku-dfn-quote-src-input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
@media (max-width: 720px) {
.wiktku-lang-row {
flex-direction: column;
}
/* Re-asserts against the skin's own mobile-breakpoint heading styles,
which load after ours in the cascade. */
#wiktku-start-form .wiktku-start-step-name {
font-size: 1.05em !important;
margin: 0 0 0.2em !important;
line-height: 1.2 !important;
border: 0 !important;
padding: 0 !important;
}
/* Single column: search box, list, and narrow textareas (Etîket,
Hevmane, Dûnde/Jê/Têkildar/Binêre herwiha word boxes, citation
source fields, …) all use the full width. */
.wiktku-native-select-wrap,
input.wiktku-ac-input,
ul.wiktku-ac-results,
#wiktku-start-form textarea.wiktku-start-narrow,
#wiktku-start-form textarea.wiktku-dfn-subfield.wiktku-start-narrow {
width: 100%;
max-width: 100%;
}
.wiktku-mod-val-input {
flex: 1 1 auto;
max-width: 100%;
}
#wiktku-start-form .wiktku-pron-wrap textarea.wiktku-pron-text,
#wiktku-start-form .wiktku-etym-wrap textarea.wiktku-etym-text {
--wiktku-ta-rows: 3;
font-size: 1em;
}
#wiktku-start-form .wiktku-tewandin-wrap textarea.wiktku-tewandin-text,
#wiktku-start-form textarea.wiktku-cavkani-text {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Mane, Nimûne + Jêgirtin and their tr/t sub-fields: keep at least 4 rows
on mobile too, rather than shrinking further than the desktop size. */
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form textarea.wiktku-dfn-nimune-text,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-tr,
#wiktku-start-form .wiktku-dfn-nimune-sub textarea.wiktku-dfn-nimune-t,
#wiktku-start-form .wiktku-dfn-jeder-group textarea.wiktku-dfn-nimune-quote,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-tr,
#wiktku-start-form .wiktku-dfn-quote-sub textarea.wiktku-dfn-nimune-quote-t {
--wiktku-ta-rows: 4;
font-size: 1em;
}
/* Citoid URL/DOI/ISBN box: taller tap target (~2 lines) on mobile,
and now that this field is a <textarea> (see start.js) its
placeholder can actually wrap into that space — a plain <input>'s
placeholder can never wrap, no matter its height. */
.wiktku-dfn-quote-citoid-input {
min-height: calc(2 * 1.35em + 10px);
font-size: 1em;
}
.wiktku-dfn-nimune-sub,
.wiktku-dfn-quote-sub {
margin-left: 0.5em;
padding-left: 0.4em;
}
.wiktku-pron-wrap .wiktku-native-select-wrap input.wiktku-ac-input,
.wiktku-etym-wrap .wiktku-native-select-wrap input.wiktku-ac-input {
font-size: 1em;
min-height: 2.2em;
}
/* Every field stacks full-width on mobile, regardless of its wide/
narrow/medium tier — there's no room to wrap several per line. */
#wiktku-start-form .wiktku-dfn-quote-src-row {
flex-basis: 100%;
}
.wiktku-start-step {
padding: 0.5em 0.6em;
}
}
/* ===================================================================
Night mode
Palette.css already flips --wikt-palette-* vars for night mode; this
hook only covers real colours (panel border) that aren't a palette var,
since a drop shadow is invisible against an equally dark page.
=================================================================== */
@media screen {
html.skin-theme-clientpref-night .wiktku-ac-results {
/* Border stands in for the shadow, invisible on a dark page. */
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-night .wiktku-start-step,
html.skin-theme-clientpref-night .wiktku-start-step:hover,
html.skin-theme-clientpref-night .wiktku-start-step:focus-within {
box-shadow: none;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .wiktku-ac-results {
border-color: var(--wikt-palette-grey-5);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.6);
}
html.skin-theme-clientpref-os .wiktku-start-step,
html.skin-theme-clientpref-os .wiktku-start-step:hover,
html.skin-theme-clientpref-os .wiktku-start-step:focus-within {
box-shadow: none;
}
}
/* "Zimanek lê zêde bike" portlet icon (Minerva/mobile) — Feather-core-edit.svg
via CSS mask, matching Minerva's own icon pattern. */
.minerva-icon-portletlink-ca-wiktku-addlang {
-webkit-mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
mask-image: url("https://upload.wikimedia.org/wikipedia/commons/d/d7/Feather-core-edit.svg");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: 20px;
mask-size: 20px;
background-color: currentColor;
background-image: none;
}
/* ── posparams step (live wikitext + helper widgets) ── */
.wiktku-posparams-wrap {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-posparams-row {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.wiktku-posparams-row label {
font-size: 1em;
min-width: 10em;
padding-right: 0;
}
.wiktku-posparams-subheading {
font-weight: 600;
font-size: 0.88em;
margin-top: 6px;
}
.wiktku-posparams-pronoun-hint {
font-size: 0.9em;
color: var(--wikt-ink-2);
font-style: italic;
}
/* Keeps a pronoun hint and its box on one line, immediately next to each
other, even when the row around them stacks label-over-field (see the
desktop grid layout for .wiktku-posparams-helpers below). */
.wiktku-posparams-field-line {
display: flex;
align-items: center;
gap: 6px;
}
.wiktku-posparams-text {
width: 100%;
box-sizing: border-box;
font-family: monospace;
font-size: 0.9em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.85em;
margin-top: 2px;
display: inline-block;
}
.wiktku-posparams-opt-panel {
display: flex;
flex-direction: column;
gap: 2px;
padding: 4px 0 2px 1em;
}
.wiktku-posparams-opt-row {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85em;
}
/* ── resim step (one or more images) ── */
.wiktku-resim-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.wiktku-resim-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.wiktku-resim-item .wiktku-posparams-row {
flex-direction: column;
align-items: flex-start;
gap: 2px;
}
.wiktku-resim-item .wiktku-posparams-row label {
min-width: 0;
font-size: 0.9em;
}
/* ── dfn: "hide/show all subsections" link ──
Toggles a class on the whole dfn stepDiv so one click hides the
etîket/kategorî/hevmane/dijmane/nimûne/jêgirtin/etc. subgroups
across every sense block at once. */
.wiktku-dfn-subs-toggle {
font-size: 0.85em;
font-weight: 500;
display: inline-block;
margin: 2px 0 8px;
color: var(--wikt-palette-blue-9, #3366cc);
text-decoration: none;
}
.wiktku-dfn-subs-toggle:hover {
text-decoration: underline;
}
/* Only the ADVANCED subgroups collapse: Nimûne, Jêgirtin, Hevmane and
Dijmane stay open because they are what people come here to fill in. */
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-dfn-subgroup-adv {
display: none !important;
}
/* ── "Malûmatên bikaranînê" accordion — fieldset-style: a legend that
floats on the box's top border (negative translateY + card-color
background "cutting through" the border line), collapsed by default.
See buildSenseBlock in start.js. ── */
#wiktku-start-form .wiktku-sense-adv-box {
margin-top: 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-card, 12px);
padding: 0 16px 16px;
}
#wiktku-start-form .wiktku-sense-adv-legend {
display: inline-flex;
align-items: center;
gap: 8px;
transform: translateY(-10px);
background: var(--wikt-card);
padding: 0 9px;
margin-left: 4px;
}
#wiktku-start-form .wiktku-sense-adv-diamond {
font-size: 12px;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-label {
font-size: 12.5px;
font-weight: 500;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-sense-adv-chevron {
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid var(--wikt-field-edge);
color: var(--wikt-ink-2);
font-size: 10px;
display: grid;
place-items: center;
background: var(--wikt-card-sunken);
cursor: pointer;
padding: 0;
}
#wiktku-start-form .wiktku-sense-adv-chevron:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
/* Collapsed-only summary: a dim one-line status, so glancing at a closed
accordion still says whether there's anything back there. Hidden while
expanded — the real fields (inside .wiktku-dfn-subgroup-adv, toggled by
li.wiktku-sense-adv-hidden above) take its place. */
#wiktku-start-form .wiktku-sense-adv-summary {
display: none;
align-items: center;
gap: 9px;
}
#wiktku-start-form li.wiktku-sense-adv-hidden .wiktku-sense-adv-summary {
display: flex;
}
#wiktku-start-form .wiktku-sense-adv-summary-dots {
letter-spacing: .18em;
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-sense-adv-summary-text {
font-size: 13.5px;
color: var(--wikt-muted);
}
/* ===================================================================
Desktop text bump
Modifier pills/buttons and posparams helper text read fine at their
small mobile sizes but look too small on desktop, so bump them up
past the 720px breakpoint only.
=================================================================== */
@media (min-width: 721px) {
.wiktku-mod-btn,
.wiktku-mod-confirm,
.wiktku-mod-cancel {
width: 1.8em;
height: 1.8em;
font-size: 1.1em;
}
.wiktku-mod-pill {
font-size: 0.95em;
padding: 3px 10px;
}
.wiktku-mod-val-input {
font-size: 0.95em;
}
.wiktku-posparams-row label {
font-size: 1em;
}
.wiktku-posparams-text {
font-size: 1em;
}
.wiktku-posparams-opt-toggle {
font-size: 0.95em;
}
.wiktku-posparams-opt-row {
font-size: 0.95em;
}
.wiktku-dfn-subs-toggle {
font-size: 0.95em;
}
/* Multi-column card layout for posparams helper fields (transliteration,
gendered forms, comparative/superlative, hanja, verb-tense roots, …),
desktop-only. Rows are shown/hidden via jQuery inline display; the
grid only rearranges visible rows, not which ones are visible. */
.wiktku-posparams-helpers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px 16px;
align-items: start;
}
.wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 3px;
}
.wiktku-posparams-helpers > .wiktku-posparams-row label {
min-width: 0;
}
.wiktku-posparams-helpers > .wiktku-posparams-row .wiktku-posparams-field,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-cinsiyet-sel,
.wiktku-posparams-helpers > .wiktku-posparams-row select.wiktku-posparams-lektur-sel {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* The "Jimar" row lives here too — just another .wiktku-posparams-row,
no extra CSS needed. */
/* These two aren't per-field rows — span the full card width so the
collapsed toggle and its checkbox list don't get squeezed into one
narrow column. */
.wiktku-posparams-helpers > .wiktku-posparams-opt-toggle,
.wiktku-posparams-helpers > .wiktku-posparams-opt-panel,
.wiktku-posparams-helpers > .wiktku-posparams-subheading {
grid-column: 1 / -1;
}
}
/* ===================================================================
SÊWIRANA NÛ — design system layer
===================================================================
Purely the visual skin (colour, type, weight, radius, spacing), layered
after the layout rules above so it wins the cascade.
No @import of Spectral / IBM Plex from Google Fonts: Wikimedia's CSP
blocks third-party font hosts, so the stacks below name the spec's
faces first (used if the wiki installs them locally) and fall back to
faces with the same feel.
Accent colour is reserved for actionable elements (focus, links, the
active tab, the primary button, chips); section headings are near-black.
=================================================================== */
#wiktku-start-form {
/* Surfaces */
--wikt-page: #f4efe5;
--wikt-card: #fffdf8;
--wikt-card-sunken: #fbf8f1;
--wikt-rule: #ded6c8; /* structural hairline */
--wikt-rule-soft: #efe8db; /* inside a card */
--wikt-field-edge: #cfc7b8;
--wikt-field-bg: #fff;
--wikt-code-bg: #f7f4ec;
--wikt-card-edge: #e6dfd2;
/* Ink */
--wikt-ink: #1c1a17;
--wikt-ink-2: #3d3830;
--wikt-ink-3: #57514a;
--wikt-muted: #8a8378;
--wikt-placeholder: #b3ab9e;
/* Accent + status */
--wikt-accent: #2a5db0;
--wikt-accent-deep: #22406f;
--wikt-accent-tint: #eef2fa;
--wikt-accent-edge: #d5def0;
--wikt-danger: #9c3a28;
--wikt-on-accent: #fff; /* text on a filled accent button */
--wikt-link-under: rgba(42, 93, 176, 0.35);
/* Shape */
--wikt-r-field: 10px;
--wikt-r-card: 14px;
--wikt-r-pill: 999px;
--wikt-tap: 44px; /* minimum field height */
--wikt-serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
--wikt-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--wikt-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-family: var(--wikt-sans);
font-size: 15px;
line-height: 1.55;
color: var(--wikt-ink);
background: var(--wikt-page);
padding: 24px;
border-radius: var(--wikt-r-card);
}
/* ── Cards ───────────────────────────────────────────────────────────
Flat cream panel held by a single hairline border, no shadow. */
#wiktku-start-form .wiktku-start-step {
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-left: 1px solid var(--wikt-card-edge);
border-radius: var(--wikt-r-card);
box-shadow: none;
padding: 24px 24px 20px;
margin-bottom: 16px;
transition: border-color 0.12s ease;
}
#wiktku-start-form .wiktku-start-step:hover,
#wiktku-start-form .wiktku-start-step:focus-within {
box-shadow: none;
border-color: var(--wikt-rule);
}
/* The fused pos + posparams pair keeps reading as one card. margin-bottom
has to be reset here too — .wiktku-start-step's own 16px otherwise wins
(higher specificity than the older #wiktku-start-pos-only rule this was
meant to override), leaving a visible gap between the two "halves". */
#wiktku-start-form #wiktku-start-pos {
border-radius: var(--wikt-r-card) var(--wikt-r-card) 0 0;
border-bottom: none;
padding-bottom: 8px;
margin-bottom: 0;
}
#wiktku-start-form #wiktku-start-posparams {
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
border-top: none;
padding-top: 4px;
}
/* ── Type ────────────────────────────────────────────────────────────
Step headings: serif, regular weight, near-black. */
#wiktku-start-form .wiktku-start-step-name {
font-family: var(--wikt-serif);
font-size: 24px !important;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
color: var(--wikt-ink);
margin: 0 0 12px;
}
/* display:block matters: this is a <label>, so it was sharing a line with
the control it introduces wherever start.js didn't emit a <br>. */
#wiktku-start-form .wiktku-start-step-question {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--wikt-ink-3);
margin: 0 0 12px;
line-height: 1.6;
max-width: 62ch;
}
/* Collapses the label entirely when empty (posparams' "Cins/zayend" title
can be blank) instead of leaving its margin behind as a gap; a no-op
for every other step, whose question text is never empty. */
#wiktku-start-form .wiktku-start-step-question:empty {
display: none;
margin: 0;
}
/* Same reason — a field label belongs above its field, not beside it. */
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
display: block;
margin-bottom: 6px;
padding-right: 0;
}
/* Field labels: small, medium weight, full-strength ink — they label a
control, so they should not read as body copy. */
#wiktku-start-form .wiktku-start-step label,
#wiktku-start-form .wiktku-posparams-row label,
#wiktku-start-form .wiktku-dfn-sublabel {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink);
letter-spacing: 0.01em;
}
/* The parenthetical/example half of a label is supporting text. */
#wiktku-start-form .wiktku-dfn-sublabel-hint,
#wiktku-start-form .wiktku-step-hint {
font-weight: 400;
font-style: italic;
color: var(--wikt-muted);
font-size: 12.5px;
}
/* ── Fields ──────────────────────────────────────────────────────────
One shape for every control: 1px edge, 10px radius, 44px tall. */
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form input.wiktku-ac-input,
#wiktku-start-form textarea,
#wiktku-start-form select {
font-family: var(--wikt-sans);
font-size: 15px;
color: var(--wikt-ink);
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
padding: 10px 14px;
box-shadow: none;
transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#wiktku-start-form input[type="text"],
#wiktku-start-form input[type="search"],
#wiktku-start-form select {
min-height: var(--wikt-tap);
}
#wiktku-start-form textarea {
line-height: 1.6;
}
/* Wikitext and anything template-shaped reads as code. */
#wiktku-start-form .wiktku-posparams-text,
#wiktku-start-form .wiktku-pron-text,
#wiktku-start-form .wiktku-etym-text,
#wiktku-start-form .wiktku-cavkani-text,
#wiktku-start-form .wiktku-tewandin-text,
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-mono);
font-size: 13.5px;
background: var(--wikt-code-bg);
border-color: var(--wikt-card-edge);
}
#wiktku-start-form ::placeholder {
color: var(--wikt-placeholder);
opacity: 1;
}
/* Focus: accent edge + 3px tint halo. The only "elevation" in the design. */
#wiktku-start-form input[type="text"]:focus,
#wiktku-start-form input[type="search"]:focus,
#wiktku-start-form textarea:focus,
#wiktku-start-form select:focus,
#wiktku-start-form input.wiktku-ac-input:focus {
border-color: var(--wikt-accent);
box-shadow: 0 0 0 3px var(--wikt-accent-tint);
outline: 0;
}
/* The searchable picker's visible input is the field, so it takes the
field shape too (it otherwise inherits an older, boxier rule). */
#wiktku-start-form input.wiktku-ac-input {
min-height: var(--wikt-tap);
border-radius: var(--wikt-r-field);
border-color: var(--wikt-field-edge);
font-size: 15px;
}
#wiktku-start-form .wiktku-ac-results {
border: 1px solid var(--wikt-rule);
border-radius: var(--wikt-r-field);
background: var(--wikt-card);
box-shadow: none;
}
#wiktku-start-form .wiktku-ac-results li.wiktku-ac-active,
#wiktku-start-form .wiktku-ac-results li:hover {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
}
/* ── Tabs → the accent's main job ────────────────────────────────────
Active tab: tint fill + 3px accent underline, per the spec's
"xala hûrgulî" note. Inactive tabs are plain text. */
#wiktku-start-form #wiktku-group-tabs,
#wiktku-start-form #wiktku-section-tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 16px;
padding-bottom: 0;
}
/* Both bars are direct children of #wiktku-start-form, so a sticky child
stays stuck for as long as any part of the form is in view. Background
is opaque so content scrolling underneath doesn't show through.
Section-tabs pins below --wiktku-siteheader-h — an offset for Vector
2022's own sticky header, which otherwise sits on top of it at top:0
(Minerva has no such header, so mobile needs no offset); group-tabs
pins below that plus --wiktku-stabs-h (section-tabs' own height). Both
custom properties are set by syncStickyTabsOffset() in start.js and
fall back to 0 on skins without a Vector sticky header. */
#wiktku-start-form #wiktku-section-tabs {
position: sticky;
top: var(--wiktku-siteheader-h, 0px);
z-index: 20;
background: var(--wikt-card);
}
#wiktku-start-form #wiktku-group-tabs {
position: sticky;
top: calc(var(--wiktku-siteheader-h, 0px) + var(--wiktku-stabs-h, 0px));
z-index: 19;
background: var(--wikt-card);
}
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
font-size: 13.5px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 9px 14px 11px;
cursor: pointer;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-group-tab:hover,
#wiktku-start-form .wiktku-section-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-group-tab.wiktku-tab-active,
#wiktku-start-form .wiktku-section-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* ── Progressive-disclosure toggles ──────────────────────────────────
Shared by "Tercîhên pêşketî" (posparams) and "Malûmatên pêşketî"
(Mane): a rotating chevron plus a count of what's hidden. */
#wiktku-start-form .wiktku-adv-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin: 4px 0 12px;
padding-top: 16px;
border-top: 1px solid var(--wikt-rule-soft);
}
#wiktku-start-form .wiktku-adv-toggle {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 500;
color: var(--wikt-accent);
text-decoration: none;
cursor: pointer;
}
#wiktku-start-form .wiktku-adv-toggle:hover .wiktku-adv-toggle-text {
text-decoration: underline;
}
#wiktku-start-form .wiktku-adv-toggle-chevron {
width: 22px;
height: 22px;
flex: 0 0 22px;
display: grid;
place-items: center;
font-size: 11px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 6px;
transform: rotate(-90deg);
transition: transform 0.15s ease;
}
/* Open state: dfn flips the class on the step, posparams on the link. */
#wiktku-start-form .wiktku-adv-toggle.wiktku-adv-open .wiktku-adv-toggle-chevron,
#wiktku-start-dfn:not(.wiktku-dfn-subs-hidden) .wiktku-adv-toggle-chevron {
transform: rotate(0deg);
}
#wiktku-start-form .wiktku-posparams-adv-panel {
border: 1px solid var(--wikt-rule-soft);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 20px 18px;
margin-bottom: 4px;
}
/* Vertical rhythm between stacked field rows (e.g. "Tewandina lêkerê"),
wider than a row's own label-to-field gap so rows read as distinct
groups instead of bleeding together. */
#wiktku-start-form .wiktku-posparams-helpers {
display: flex;
flex-direction: column;
gap: 14px;
}
/* ── posparams grid ──────────────────────────────────────────────────
"Tor, ne rêz" — two columns, nothing narrower than 220px, so the six
gender/number fields stop being a six-across scanline. */
@media (min-width: 721px) {
#wiktku-start-form .wiktku-posparams-helpers,
#wiktku-start-form .wiktku-posparams-adv-panel {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px 22px;
align-items: start;
}
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row {
flex-direction: column;
align-items: stretch;
flex-wrap: nowrap;
gap: 7px;
}
/* The row's own gap already separates the label from its field — the
label's margin-bottom (set for the stacked mobile layout) would
otherwise double that spacing here. */
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-row label,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-row label {
margin-bottom: 0;
}
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-adv-toggle-row,
#wiktku-start-form .wiktku-posparams-helpers > .wiktku-posparams-adv-panel,
#wiktku-start-form .wiktku-posparams-adv-panel > .wiktku-posparams-opt-panel {
grid-column: 1 / -1;
}
}
#wiktku-start-form .wiktku-posparams-opt-panel {
border-top: 1px solid var(--wikt-rule-soft);
padding-top: 14px;
margin-top: 4px;
}
#wiktku-start-form .wiktku-posparams-opt-row {
font-size: 13px;
color: var(--wikt-ink-3);
}
/* ── Sense blocks ────────────────────────────────────────────────────
Each sense is a sunken panel inside the Mane card, with its number as
an accent counter — so "how many meanings am I writing" is legible at
a glance. */
#wiktku-start-form .wiktku-dfn-subgroup {
margin: 14px 0 0;
padding-top: 14px;
border-top: 1px solid var(--wikt-rule-soft);
}
/* No divider above the first item — it would be a rule against the
sense's own text. Needed explicitly: the earlier :first-of-type rule
is a class selector and loses to this id-scoped one. */
#wiktku-start-form .wiktku-dfn-subgroup:first-of-type {
margin-top: 8px;
padding-top: 0;
border-top: none;
}
#wiktku-start-form .wiktku-dfn-sublabel-term {
color: var(--wikt-ink);
}
/* Plain white field like any other, not a tinted fill — a tint would
read as "disabled" next to the white Mane box above it. */
#wiktku-start-form .wiktku-dfn-subfield,
#wiktku-start-form textarea.wiktku-dfn-mane,
#wiktku-start-form .wiktku-word-input {
background: var(--wikt-field-bg);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-field);
}
/* No "1." "2." markers on the sub-lists: numbered rows would read as a
form to fill top-down, when each row is really "another one if you
have one" — and the marker would land on its own line above the
block-level textarea, looking like a stray character. */
#wiktku-start-form .wiktku-dfn-sublist {
margin: 0;
padding-left: 0;
list-style: none;
}
#wiktku-start-form .wiktku-dfn-sublist > li {
margin-bottom: 6px;
}
/* ── Chips: tags, modifiers, template pills ──────────────────────────── */
#wiktku-start-form .wiktku-mod-pill,
#wiktku-start-form .wiktku-besendin-select + .wiktku-chip {
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 13px;
padding: 6px 12px;
}
#wiktku-start-form .wiktku-mod-btn,
#wiktku-start-form .wiktku-mod-confirm,
#wiktku-start-form .wiktku-mod-cancel {
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-radius: 8px;
}
/* ── Chip + toggle fields ("1c" design) — Dever/Şîve, Etîket, Kategorî:
a small "+ …" toggle on the sense, not an always-visible field (see
buildChipField in start.js, used by all three). Chips sit inline before
the toggle; clicking it opens a compact panel with optional quick-pick
suggestions plus free text below. Each field's own wrap class (e.g.
.wiktku-dfn-dever-wrap) only scopes chip reads/writes in start.js — all
the visual styling here is shared via .wiktku-dfn-chipfield-*. The three
toggles live together in .wiktku-dfn-chipfield-row; their panels render
separately in .wiktku-dfn-chipfield-panels below it, so opening one
field's panel never pushes the other toggles around. ── */
#wiktku-start-form .wiktku-dfn-chipfield-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 8px 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-panels {
display: flex;
flex-direction: column;
gap: 8px;
}
#wiktku-start-form .wiktku-dfn-chipfield-wrap {
margin: 0;
}
#wiktku-start-form .wiktku-dfn-chipfield-chips {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
#wiktku-start-form .wiktku-dfn-chipfield-chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--wikt-accent-tint);
color: var(--wikt-accent-deep);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-pill);
font-size: 12px;
padding: 3px 6px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle {
font-size: 12px;
color: var(--wikt-ink-2);
background: var(--wikt-card);
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 10px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-toggle:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-dfn-chipfield-panel {
margin-top: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
border-radius: var(--wikt-r-card, 6px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 10px;
/* max-width bounds .wiktku-dfn-chipfield-suggest so its pills (e.g.
Dever/Şîve's 11 suggestions) wrap onto multiple lines instead of
stretching the panel into one long unbroken row. */
min-width: 260px;
max-width: 340px;
}
#wiktku-start-form .wiktku-dfn-chipfield-hint {
font-size: 13px;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-dfn-chipfield-suggest {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
cursor: pointer;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
padding: 3px 8px 3px 10px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove {
border: 0;
background: transparent;
color: var(--wikt-muted);
font-size: 13px;
line-height: 1;
cursor: pointer;
padding: 0 2px;
}
#wiktku-start-form .wiktku-dfn-chipfield-pill-remove:hover {
color: var(--wikt-danger);
}
#wiktku-start-form .wiktku-dfn-chipfield-pill:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-dfn-chipfield-input-row {
display: flex;
gap: 6px;
}
#wiktku-start-form .wiktku-dfn-chipfield-input {
flex: 1;
/* Without this, flexbox gives a text input an automatic minimum size
based on its default content width, which is wider than this panel
ever gets on a phone — the row can't shrink to fit and "Qeyd bike"
gets pushed out past the card's edge instead of wrapping/shrinking. */
min-width: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: 2px;
font-size: 13px;
padding: 6px 8px;
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-chipfield-add {
flex-shrink: 0;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
padding: 6px 14px;
cursor: pointer;
}
#wiktku-start-form .wiktku-dfn-chipfield-add:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── Footer + buttons ────────────────────────────────────────────────
One primary filled pill; destructive actions are outline-only in the
danger colour so they stay quiet next to it. */
#wiktku-start-form #wiktku-start-footer {
background: var(--wikt-card-sunken);
border-top: 1px solid var(--wikt-rule);
border-radius: 0 0 var(--wikt-r-card) var(--wikt-r-card);
padding: 16px 20px;
gap: 10px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
border-radius: var(--wikt-r-pill) !important;
font-family: var(--wikt-sans);
font-size: 14px;
font-weight: 500;
padding: 11px 22px;
box-shadow: none !important;
min-height: var(--wikt-tap);
}
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
background: var(--wikt-accent) !important;
border-color: var(--wikt-accent) !important;
color: var(--wikt-on-accent) !important;
}
/* Destructive actions: outline, not fill. */
#wiktku-start-form #wiktku-start-footer .oo-ui-flaggedElement-destructive .oo-ui-buttonElement-button {
background: var(--wikt-card) !important;
border-color: var(--wikt-danger) !important;
color: var(--wikt-danger) !important;
}
#wiktku-start-form #wiktku-last-saved {
font-family: var(--wikt-mono);
font-size: 12px;
color: var(--wikt-muted);
}
/* ── Beşên din picker ────────────────────────────────────────────────
This dropdown is now the entry point to eighteen sections, so it gets
room and a label rather than sitting flush under the tab bar. */
#wiktku-start-form .wiktku-besendin-picker {
margin: 0 0 20px;
padding: 16px 18px;
background: var(--wikt-card);
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-besendin-picker .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-besendin-picker input.wiktku-ac-input {
width: 100%;
max-width: 28em;
}
/* ── Notices ─────────────────────────────────────────────────────────── */
#wiktku-start-form .wiktku-addlang-notice {
background: var(--wikt-accent-tint);
border: 1px solid var(--wikt-accent-edge);
border-radius: var(--wikt-r-field);
color: var(--wikt-ink-2);
padding: 12px 16px;
margin-bottom: 16px;
}
#wiktku-start-form .wiktku-pron-preview {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent-edge);
border-radius: 12px;
}
#wiktku-start-form .wiktku-pron-preview-confirm,
#wiktku-start-form .wiktku-pron-preview-cancel {
border-radius: var(--wikt-r-pill);
padding: 8px 16px;
min-height: 36px;
}
/* Links carry the accent and a soft underline, per the spec's `a` rule. */
#wiktku-start-form a {
color: var(--wikt-accent);
text-decoration: none;
border-bottom: 1px solid var(--wikt-link-under);
}
#wiktku-start-form a:hover {
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-adv-toggle,
#wiktku-start-form .wiktku-group-tab,
#wiktku-start-form .wiktku-section-tab {
border-bottom-style: solid;
}
#wiktku-start-form .wiktku-adv-toggle {
border-bottom: 0;
}
/* ===================================================================
Dark mode
===================================================================
Every colour above is a token on #wiktku-start-form, so this is a
swap of that one block.
Only applies given an explicit dark signal (skin-theme-clientpref-night /
skin-nightmode, or skin-theme-clientpref-os plus prefers-color-scheme:
dark) — no bare prefers-color-scheme fallback, since legacy Vector sets
no theme class either and would otherwise go dark with nothing else on
the page to match.
Palette is warm, not neutral grey, to keep the cream design's character.
Accent lightens to #7ba7e8 since #2a5db0 on a dark card is ~2.4:1
contrast and reads as near-black.
=================================================================== */
html.skin-theme-clientpref-night #wiktku-start-form,
html.skin-nightmode #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form {
--wikt-page: #171613;
--wikt-card: #201e1a;
--wikt-card-sunken: #1a1916;
--wikt-card-edge: #322e28;
--wikt-rule: #3a3630;
--wikt-rule-soft: #2c2924;
--wikt-field-edge: #4a453d;
--wikt-field-bg: #191815;
--wikt-code-bg: #14130f;
--wikt-ink: #ece7dd;
--wikt-ink-2: #d5cfc4;
--wikt-ink-3: #b3aca1;
--wikt-muted: #8f887c;
--wikt-placeholder: #6d675e;
--wikt-accent: #7ba7e8;
--wikt-accent-deep: #cfe0f8;
--wikt-accent-tint: #1c2839;
--wikt-accent-edge: #35496b;
--wikt-danger: #e5907c;
--wikt-on-accent: #14213a;
--wikt-link-under: rgba(123, 167, 232, 0.4);
}
}
/* A handful of rules still name literal light colours instead of tokens;
neutralise the ones that actually show through in night mode. */
html.skin-theme-clientpref-night #wiktku-start-form .wiktku-ac-results,
html.skin-nightmode #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os #wiktku-start-form .wiktku-ac-results {
background: var(--wikt-card);
border-color: var(--wikt-rule);
color: var(--wikt-ink);
}
}
/* ===================================================================
Autocomplete list: stop it escaping its card
===================================================================
A fixed width would run past the card's border on full-width pickers,
since the list is absolutely positioned at the wrapper's left edge.
width:100% ties it to the (shrink-to-fit) wrapper's input width instead;
min/max-width keep short pickers readable and stop viewport overflow. */
#wiktku-start-form ul.wiktku-ac-results {
width: 100%;
min-width: min(18em, 100%);
max-width: min(92vw, 34em);
box-sizing: border-box;
border-radius: var(--wikt-r-field);
box-shadow: none;
}
/* And the wrapper itself must not exceed the card it sits in. */
#wiktku-start-form .wiktku-native-select-wrap {
max-width: 100%;
}
#wiktku-start-form .wiktku-cavkani-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-cavkani-wrap input.wiktku-ac-input,
#wiktku-start-form .wiktku-pron-wrap .wiktku-native-select-wrap,
#wiktku-start-form .wiktku-etym-wrap .wiktku-native-select-wrap {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
/* ===================================================================
Mobile footer
===================================================================
Below 720px the primary button keeps its full size and everything
else shrinks to a secondary scale, so the sticky footer doesn't
dominate a phone viewport.
=================================================================== */
@media (max-width: 720px) {
#wiktku-start-form {
padding: 12px;
font-size: 14px;
}
#wiktku-start-form .wiktku-start-step {
padding: 16px 14px 14px;
border-radius: 12px;
}
#wiktku-start-form .wiktku-start-step-name {
font-size: 20px !important;
}
#wiktku-start-form #wiktku-start-footer {
padding: 10px 12px;
gap: 6px;
row-gap: 8px;
}
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement-button {
font-size: 13px;
padding: 8px 14px;
min-height: 38px;
}
/* The one you actually press stays thumb-sized. */
#wiktku-start-form #wiktku-unhide-editform .oo-ui-buttonElement-button {
font-size: 14px;
padding: 10px 20px;
min-height: var(--wikt-tap);
}
/* Secondary actions share a row instead of each claiming a full one. */
#wiktku-start-form #wiktku-start-footer .oo-ui-buttonElement {
flex: 0 1 auto;
}
#wiktku-start-form #wiktku-last-saved {
flex: 1 0 100%;
order: 10;
font-size: 11px;
white-space: normal;
}
#wiktku-start-form .wiktku-adv-toggle-row {
gap: 8px;
}
}
/* ===================================================================
IPA character palette (v3 spec)
===================================================================
Click-to-insert chart under Bilêvkirin, after fr.wiktionary's. Two
sub-tabs — a short Kurmancî set that opens by default, and the full
chart — because ~20 of the ~180 IPA symbols ever occur in Kurmancî
and showing all of them first is the same "wall of fields" problem
this redesign exists to fix.
=================================================================== */
#wiktku-start-form .wiktku-ipa-panel {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card-sunken);
padding: 0 0 12px;
margin-bottom: 8px;
overflow: hidden;
}
#wiktku-start-form .wiktku-ipa-subtabs {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px 0;
border-bottom: 1px solid var(--wikt-rule-soft);
margin-bottom: 12px;
flex-wrap: wrap;
}
#wiktku-start-form .wiktku-ipa-subtab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
padding: 7px 12px 9px;
border-bottom: 3px solid transparent;
border-radius: 8px 8px 0 0;
cursor: pointer;
}
#wiktku-start-form .wiktku-ipa-subtab:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-ipa-subtab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Templates are not characters, so they sit apart from the chart —
pinned to the right of the sub-tab row. */
#wiktku-start-form .wiktku-ipa-tplbtns {
margin-left: auto;
display: flex;
gap: 6px;
padding-bottom: 6px;
}
#wiktku-start-form .wiktku-ipa-tplbtn {
font-family: var(--wikt-mono);
font-size: 12px;
padding: 6px 10px;
border-radius: 7px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
cursor: pointer;
white-space: nowrap;
}
#wiktku-start-form .wiktku-ipa-tplbtn:hover {
border-color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-ipa-grid {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 12px;
}
#wiktku-start-form .wiktku-ipa-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 74px;
font-size: 11.5px;
color: var(--wikt-muted);
padding-top: 8px;
line-height: 1.3;
}
#wiktku-start-form .wiktku-ipa-keys {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex: 1 1 auto;
}
/* Full IPA chart: a dropdown picks one group (Bêdeng 1, Bêdeng 2, …)
instead of showing all ~180 symbols' rows at once. */
#wiktku-start-form .wiktku-ipa-group-row {
padding-bottom: 4px;
}
#wiktku-start-form select.wiktku-ipa-group-select {
width: 14em;
max-width: 100%;
}
#wiktku-start-form .wiktku-ipa-key {
width: 30px;
height: 30px;
padding: 0;
border-radius: 8px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-field-bg);
color: var(--wikt-ink);
font-family: var(--wikt-mono);
font-size: 14px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
transition: background 0.1s ease, border-color 0.1s ease;
}
#wiktku-start-form .wiktku-ipa-key:hover {
background: var(--wikt-accent-tint);
border-color: var(--wikt-accent);
color: var(--wikt-accent-deep);
}
#wiktku-start-form .wiktku-ipa-key:active {
transform: translateY(1px);
}
/* Ligatures like d͡ʒ and long vowels need a little more room than one
glyph; let those keys grow instead of clipping. */
#wiktku-start-form .wiktku-ipa-key {
width: auto;
min-width: 30px;
padding: 0 5px;
}
/* A combining mark is shown on a dotted circle so it has something to
attach to; only the mark itself is inserted. */
#wiktku-start-form .wiktku-ipa-key-combining {
color: var(--wikt-ink-2);
}
@media (max-width: 720px) {
#wiktku-start-form .wiktku-ipa-row {
flex-direction: column;
gap: 3px;
}
#wiktku-start-form .wiktku-ipa-rowlabel {
flex: 0 0 auto;
padding-top: 0;
}
#wiktku-start-form .wiktku-ipa-key {
min-width: 34px;
height: 34px;
}
}
/* ── "+ Beş zêde bike" menu ──────────────────────────────────────────
Adding a part of speech and adding Bilêvkirin are different actions,
so the button opens a small menu instead of doing one of them. */
#wiktku-start-form .wiktku-addsection-menu {
position: absolute;
z-index: 60;
margin-top: 6px;
min-width: 15em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 10px;
padding: 4px;
display: flex;
flex-direction: column;
}
#wiktku-start-form .wiktku-addsection-item {
display: flex;
flex-direction: column;
gap: 1px;
padding: 8px 10px;
border-radius: 7px;
border-bottom: 0;
text-decoration: none;
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-addsection-item:hover {
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-addsection-item-label {
font-size: 13.5px;
font-weight: 500;
}
#wiktku-start-form .wiktku-addsection-item-hint {
font-size: 11.5px;
color: var(--wikt-muted);
}
#wiktku-start-form #wiktku-section-tabs .oo-ui-buttonElement {
position: relative;
}
/* Bilêvkirin is a peer of the POS sections but not a part of speech —
a little extra space before it marks the boundary, no divider line. */
#wiktku-start-form .wiktku-section-tab-pron {
margin-left: 6px;
}
/* ===================================================================
Per-sense tabs
===================================================================
"Mane 1 / Mane 2 / + Mane zêde bike" switches which sense is shown;
only the active one is in the flow, so a multi-sense entry doesn't
turn into a long scroll. */
#wiktku-start-form .wiktku-dfn-sense-tabs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-bottom: 1px solid var(--wikt-rule);
margin-bottom: 14px;
}
#wiktku-start-form .wiktku-dfn-sense-tab {
font-size: 13px;
font-weight: 500;
color: var(--wikt-ink-3);
background: transparent;
border: 0;
border-bottom: 3px solid transparent;
border-radius: var(--wikt-r-pill) var(--wikt-r-pill) 0 0;
padding: 7px 12px 9px;
cursor: pointer;
user-select: none;
transition: background 0.12s ease, color 0.12s ease;
}
#wiktku-start-form .wiktku-dfn-sense-tab:hover {
background: var(--wikt-card-sunken);
color: var(--wikt-ink);
}
#wiktku-start-form .wiktku-dfn-sense-tab.wiktku-tab-active {
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom-color: var(--wikt-accent);
}
/* Delete button on the Mane / Beş tab bars — a real OOUI ButtonWidget
(icon: "trash", flags: ["destructive"], framed: false), so its glyph,
red destructive tint and hover/focus states all come from OOUI's own
WikimediaUI theme rather than being reimplemented here. This class
only places it: a little breathing room from the tab before it, and
vertically centered in the flex tab bar (OOUI's own icon-button box
is already close to the tabs' height, so no extra sizing is needed). */
#wiktku-start-form .wiktku-trash-btn {
margin-left: 4px;
}
#wiktku-start-form li.wiktku-dfn-sense {
border: 1px solid var(--wikt-card-edge);
border-radius: 12px;
background: var(--wikt-card);
margin-bottom: 10px;
padding: 14px;
list-style: none;
overflow: visible;
}
#wiktku-start-form li.wiktku-dfn-sense.wiktku-sense-collapsed {
display: none;
}
/* ===================================================================
Template rows in the pickers (v3 spec)
===================================================================
One row shape everywhere a template is chosen: the ⓘ explains terms
like "guharto/kevnbûyî/ku-Latn/şaşnivîs" without leaving the form,
and the 🔗 links out for when it doesn't.
=================================================================== */
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
white-space: normal;
padding: 6px 10px;
}
#wiktku-start-form .wiktku-ac-tpltext {
flex: 1 1 auto;
min-width: 0;
font-family: var(--wikt-mono);
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Status, not category: green is the one to reach for, yellow means
"only if you specifically need it" (kevnbûyî, şaşnivîs, kêmpeyda). */
#wiktku-start-form .wiktku-tpl-dot {
flex: 0 0 8px;
width: 8px;
height: 8px;
border-radius: 999px;
}
#wiktku-start-form .wiktku-tpl-dot-ok { background: #3d8a55; }
#wiktku-start-form .wiktku-tpl-dot-special { background: #c9971f; }
#wiktku-start-form .wiktku-tpl-icons {
flex: 0 0 auto;
display: flex;
gap: 5px;
align-items: center;
}
#wiktku-start-form .wiktku-tpl-icon {
width: 26px;
height: 26px;
display: grid;
place-items: center;
font-size: 12px;
line-height: 1;
cursor: pointer;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
color: var(--wikt-accent);
border-bottom: 1px solid var(--wikt-accent-edge);
text-decoration: none;
padding: 0;
}
#wiktku-start-form .wiktku-tpl-icon-link { border-radius: 7px; }
#wiktku-start-form .wiktku-tpl-icon-info {
border-radius: 999px;
font-family: var(--wikt-serif);
font-style: italic;
font-weight: 600;
background: transparent;
border-color: var(--wikt-field-edge);
color: var(--wikt-muted);
}
#wiktku-start-form .wiktku-tpl-icon:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
/* ── The ⓘ panel ─────────────────────────────────────────────────────
Three parts, in the spec's order: what it does, its parameters, and a
way out to the template page. */
#wiktku-start-form .wiktku-tpl-info {
position: relative;
margin: 8px 0 4px;
padding: 14px 16px;
border: 1px solid var(--wikt-accent-edge);
background: var(--wikt-accent-tint);
border-radius: 12px;
font-size: 13.5px;
line-height: 1.6;
color: var(--wikt-ink-2);
}
#wiktku-start-form .wiktku-tpl-info-name {
font-family: var(--wikt-mono);
font-size: 13px;
color: var(--wikt-accent-deep);
margin-bottom: 6px;
}
#wiktku-start-form .wiktku-tpl-info-params {
margin-top: 8px;
}
#wiktku-start-form .wiktku-tpl-info-params-title {
font-size: 12px;
color: var(--wikt-muted);
margin-bottom: 2px;
}
#wiktku-start-form .wiktku-tpl-info-params ul {
margin: 0;
padding-left: 18px;
font-family: var(--wikt-mono);
font-size: 12.5px;
}
#wiktku-start-form .wiktku-tpl-info-link {
display: inline-block;
margin-top: 10px;
font-size: 13px;
}
#wiktku-start-form .wiktku-tpl-info-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border-radius: 999px;
border: 0;
background: transparent;
color: var(--wikt-muted);
cursor: pointer;
font-size: 13px;
line-height: 1;
}
#wiktku-start-form .wiktku-tpl-info-close:hover {
background: var(--wikt-card);
color: var(--wikt-ink);
}
/* ===================================================================
Footer overflow "⋯" (v3 spec)
===================================================================
Bişîne and "Edîtora çavkaniyê veke" stay on the bar; the cache
actions, saved-at note and Tercîh move in here so the destructive
buttons don't compete for attention and the sticky footer stays
to one row on a phone.
=================================================================== */
#wiktku-start-form .wiktku-more-wrap {
position: relative;
margin-left: auto;
flex: 0 0 auto;
}
#wiktku-start-form .wiktku-more-btn {
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid var(--wikt-field-edge);
background: var(--wikt-card);
color: var(--wikt-muted);
font-size: 16px;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
}
#wiktku-start-form .wiktku-more-btn:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
}
#wiktku-start-form .wiktku-more-menu {
position: absolute;
right: 0;
bottom: calc(100% + 8px);
z-index: 70;
min-width: 17em;
background: var(--wikt-card);
border: 1px solid var(--wikt-rule);
border-radius: 12px;
padding: 6px;
display: flex;
flex-direction: column;
gap: 2px;
}
#wiktku-start-form .wiktku-more-item {
display: flex;
align-items: center;
gap: 8px;
}
/* The buttons inside the menu are menu rows, not buttons on a bar. */
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement {
flex: 1 1 auto;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button {
display: block;
width: 100%;
text-align: left;
border: 0 !important;
background: transparent !important;
border-radius: 7px !important;
padding: 9px 10px;
min-height: 0;
font-weight: 400;
color: var(--wikt-ink) !important;
}
#wiktku-start-form .wiktku-more-item .oo-ui-buttonElement-button:hover {
background: var(--wikt-accent-tint) !important;
}
#wiktku-start-form .wiktku-more-item-danger .oo-ui-buttonElement-button {
color: var(--wikt-danger) !important;
}
#wiktku-start-form .wiktku-more-item-prefs .wiktku-prefs-btn {
width: 26px;
height: 26px;
flex: 0 0 26px;
border: 0;
background-color: transparent;
}
#wiktku-start-form .wiktku-more-item-label {
font-size: 13.5px;
}
#wiktku-start-form .wiktku-more-sep {
height: 1px;
background: var(--wikt-rule-soft);
margin: 4px 2px;
}
#wiktku-start-form .wiktku-more-note {
padding: 4px 10px 2px;
}
#wiktku-start-form .wiktku-more-note #wiktku-last-saved {
white-space: normal;
}
/* "+ Mane zêde bike" sits as the last item in .wiktku-dfn-sense-tabs. */
#wiktku-start-form .wiktku-add-sense {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 3px 0;
padding: 6px 14px;
border: 1px solid var(--wikt-field-edge);
border-radius: var(--wikt-r-pill);
background: var(--wikt-card);
color: var(--wikt-ink-2);
font-family: var(--wikt-sans);
font-size: 13px;
cursor: pointer;
min-height: 34px;
}
#wiktku-start-form .wiktku-add-sense:hover {
border-color: var(--wikt-accent);
color: var(--wikt-accent);
background: var(--wikt-accent-tint);
}
#wiktku-start-form .wiktku-add-sense-plus {
color: var(--wikt-accent);
font-size: 15px;
line-height: 1;
}
/* "+ Beşeke din zêde bike" and "Bi otomatîk tijî bike" are OO.ui.ButtonWidgets
(see start.js), which otherwise render with OOUI's own framed chrome —
override the inner .oo-ui-buttonElement-button to match .wiktku-add-sense's
flat-pill shape above instead, same !important pattern already used for
#wiktku-start-footer's buttons. */
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button {
border: 1px solid var(--wikt-field-edge) !important;
border-radius: var(--wikt-r-pill) !important;
background: var(--wikt-card) !important;
color: var(--wikt-ink-2) !important;
font-family: var(--wikt-sans);
font-size: 13px;
font-weight: normal;
padding: 6px 14px;
min-height: 34px;
box-shadow: none !important;
}
#wiktku-start-form .wiktku-add-sense-btn .oo-ui-buttonElement-button:hover {
border-color: var(--wikt-accent) !important;
color: var(--wikt-accent) !important;
background: var(--wikt-accent-tint) !important;
}
/* ── Şablon picker, demoted to a link ────────────────────────────────
Picking a template replaces the definition rather than adding to it,
so it sits above the Mane box as a quiet alternative route, hidden
behind one line of text until it is actually wanted. */
#wiktku-start-form .wiktku-dfn-tpl-link {
display: inline-block;
margin-bottom: 8px;
font-size: 12.5px;
color: var(--wikt-muted);
border-bottom: 1px solid transparent;
}
#wiktku-start-form .wiktku-dfn-tpl-link:hover {
color: var(--wikt-accent);
border-bottom-color: var(--wikt-link-under);
}
#wiktku-start-form .wiktku-dfn-tpl-row {
margin-bottom: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--wikt-rule-soft);
}
/* ── Template-row icons on touch screens ─────────────────────────────
26px is fine for a mouse and too small for a thumb, and these two sit
right next to each other at the end of a row you can also tap to
select. Give them real targets and some separation on small screens. */
@media (max-width: 720px), (pointer: coarse) {
#wiktku-start-form .wiktku-tpl-icon {
width: 34px;
height: 34px;
font-size: 14px;
}
#wiktku-start-form .wiktku-tpl-icons {
gap: 8px;
padding-left: 4px;
}
#wiktku-start-form ul.wiktku-ac-results li.wiktku-ac-tplrow {
min-height: 50px;
}
}
/* The rendered pronunciation preview is real parser output, so it needs
to sit as content rather than in the monospace wikitext treatment the
panel inherits. */
#wiktku-start-form .wiktku-pron-preview-body {
font-family: var(--wikt-sans);
font-size: 15px;
background: var(--wikt-card);
border: 1px solid var(--wikt-accent-edge);
border-radius: 8px;
padding: 10px 12px;
white-space: normal;
}
#wiktku-start-form .wiktku-pron-preview-body .IPA,
#wiktku-start-form .wiktku-pron-preview-body code,
#wiktku-start-form .wiktku-pron-preview-body tt {
font-family: var(--wikt-mono);
}
/* Parser output brings its own margins; strip the outer ones so the
preview box doesn't gain a blank first line. */
#wiktku-start-form .wiktku-pron-preview-body > *:first-child { margin-top: 0; }
#wiktku-start-form .wiktku-pron-preview-body > *:last-child { margin-bottom: 0; }
sw27z53u3vytgqc0zf4314c835pi8dp
Bikarhêner:Wikihez/Gadget-test.js
2
1358395
6334088
6333908
2026-08-16T12:38:40Z
Wikihez
7344
6334088
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage)
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) {}
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû/vala dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qadan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", not one per tab
// — deletes whichever sense/section is currently open. A trash icon on
// every individual tab put the hit-target right where people click to
// switch tabs, which is exactly how a stray click ended up deleting the
// wrong one; a single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê ravek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (pêwîst)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> bê nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: qualifier (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: qualifier (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle — an ji pêşkêşkariya jêrîn şablonekî hilbijêre, ew ê bixweber li vir were nivîsandin."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Kît/stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Dengdêr", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Dengdêr", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tîpên bilêvkirinê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertAtCursor(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function (stepDiv) {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
dfnArea.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
// The full refresh() (which rebuilds Werger/Bi zaravayên din —
// glossList()/build() re-reads every sense from the DOM) is
// deferred to blur rather than run on every keystroke; see
// onDfnInput for why that's safe.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Grow the sense list when the title field of the last block gets content,
// mirroring what onDfnInput does for the plain mane textarea.
tplTitleInput.on("input", function () {
steps.dfn.onDfnInput(stepDiv, block, this);
});
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Ev ne pênase ye — şablonek bi kar bîne");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
var lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
// Deliberately does nothing on every keystroke. The only thing a
// live Mane/template-title edit needs to propagate is the Werger/
// Bi zaravayên din gloss list (glossList()/build() re-read every
// sense in the DOM, and get slow with more than one sense around)
// — mane itself isn't a required step, so nothing else in
// refresh() depends on it changing mid-keystroke. The callers
// (dfnArea/tercimeArea/izahArea/tplTitleInput) run refresh() on
// blur instead; getWikitext()/output() always re-read the DOM
// fresh regardless, so the final wikitext is correct either way.
onDfnInput: function (stepDiv, block, textarea) {},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
var idx = defLines.length;
var cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
var idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
var cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
// Switching tabs can shorten the page (e.g. leaving a long
// "Beşên din" field), and the browser may clamp the scroll
// position on its own timing before our measurement runs.
// Deferring one frame lets that settle first, so the
// offsetTop read below is the final, post-refresh one.
window.requestAnimationFrame(function () {
var bar = document.getElementById("wiktku-group-tabs");
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
var stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
var stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
a2n5qchtagys3hfrumdxe7i6gint0xz
6334097
6334088
2026-08-16T13:54:30Z
Wikihez
7344
6334097
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû/vala dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qadan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", not one per tab
// — deletes whichever sense/section is currently open. A trash icon on
// every individual tab put the hit-target right where people click to
// switch tabs, which is exactly how a stray click ended up deleting the
// wrong one; a single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê ravek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (pêwîst)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> bê nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: qualifier (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: qualifier (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle — an ji pêşkêşkariya jêrîn şablonekî hilbijêre, ew ê bixweber li vir were nivîsandin."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Kît/stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Dengdêr", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Dengdêr", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tîpên bilêvkirinê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertAtCursor(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Ev ne pênase ye — şablonek bi kar bîne");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
// Switching tabs can shorten the page (e.g. leaving a long
// "Beşên din" field), and the browser may clamp the scroll
// position on its own timing before our measurement runs.
// Deferring one frame lets that settle first, so the
// offsetTop read below is the final, post-refresh one.
window.requestAnimationFrame(function () {
var bar = document.getElementById("wiktku-group-tabs");
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
7c3dx1ujnq6y0j5c5vbjocajo7sx3bc
6334100
6334097
2026-08-16T14:13:22Z
Wikihez
7344
6334100
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertAtCursor(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Ev ne pênase ye — şablonek bi kar bîne");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
// Switching tabs can shorten the page (e.g. leaving a long
// "Beşên din" field), and the browser may clamp the scroll
// position on its own timing before our measurement runs.
// Deferring one frame lets that settle first, so the
// offsetTop read below is the final, post-refresh one.
window.requestAnimationFrame(function () {
var bar = document.getElementById("wiktku-group-tabs");
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
r952gzdzors4luhq7milf506c60gjgx
6334101
6334100
2026-08-16T14:15:58Z
Wikihez
7344
6334101
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Ev ne pênase ye — şablonek bi kar bîne");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
// Switching tabs can shorten the page (e.g. leaving a long
// "Beşên din" field), and the browser may clamp the scroll
// position on its own timing before our measurement runs.
// Deferring one frame lets that settle first, so the
// offsetTop read below is the final, post-refresh one.
window.requestAnimationFrame(function () {
var bar = document.getElementById("wiktku-group-tabs");
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
cr2qxdsjhpc2ahany5qbc73bn3sg760
6334109
6334101
2026-08-16T15:00:51Z
Wikihez
7344
6334109
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Ev ne pênase ye — şablonek bi kar bîne");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplLabelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var labelTpl = buildSenseTpl(sense, "etiket", lang, "e");
var out = "# " + (katTpl ? katTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
gld0vwqel0e6w9xjnvubypsumlwkwwu
6334119
6334109
2026-08-16T15:37:25Z
Wikihez
7344
6334119
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve chips ("1c" design: a small "+ Dever/Şîve" toggle
// on the sense, rather than an always-visible field — most senses
// have none, so this doesn't lengthen the form for them. Picked/
// typed values render as chips; the toggle opens a small panel
// with quick-pick suggestions plus free text. See getOutput() for
// how these fold into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverWrap = $("<div class='wiktku-dfn-dever-wrap'></div>");
var deverChips = $("<span class='wiktku-dfn-dever-chips'></span>");
var deverToggle = $("<button type='button' class='wiktku-dfn-dever-toggle'>+ Dever/Şîve</button>");
var deverPanel = $("<div class='wiktku-dfn-dever-panel'></div>").hide();
var deverSuggest = $("<div class='wiktku-dfn-dever-suggest'></div>");
var deverInput = $("<input type='text' class='wiktku-dfn-dever-input'>")
.attr("placeholder", "An ya nû binivîse…");
var deverAddBtn = $("<button type='button' class='wiktku-dfn-dever-add'>Tomar</button>");
function deverValues() {
var vals = [];
deverChips.find(".wiktku-dfn-dever-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderDeverSuggest() {
var taken = deverValues();
deverSuggest.empty();
$.each(DEVER_SUGGESTIONS, function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-dever-pill'></span>").text(name);
pill.on("click", function () {
addDeverChip(name);
deverInput.val("").focus();
});
deverSuggest.append(pill);
});
}
/** Adds one Dever/Şîve chip (picked from suggestions or typed);
* also called from restoreDfn() via block.data("addDeverChip"). */
function addDeverChip(val) {
val = $.trim(val || "");
if (!val || deverValues().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-dever-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-dever-remove' title='Rake'>×</button>");
remove.on("click", function () {
chip.remove();
renderDeverSuggest();
});
chip.append(remove);
chip.insertBefore(deverToggle); // chips in insertion order, toggle stays last
renderDeverSuggest();
}
block.data("addDeverChip", addDeverChip);
deverToggle.on("click", function () {
if (deverPanel.is(":visible")) {
deverPanel.hide();
return;
}
renderDeverSuggest();
deverPanel.show();
deverInput.focus();
});
function commitDeverInput() {
addDeverChip(deverInput.val());
deverInput.val("").focus();
}
deverAddBtn.on("click", commitDeverInput);
deverInput.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitDeverInput(); }
});
// Close the panel once focus leaves the whole widget — same
// pattern as the ⊕ modifier picker.
deverWrap.on("focusout", function () {
setTimeout(function () {
if (!$.contains(deverWrap[0], document.activeElement)) deverPanel.hide();
}, 150);
});
deverPanel.append(
$("<div class='wiktku-dfn-dever-hint'></div>").text("Ev mane li ku derê an di kîjan devokê de tê bikaranîn?")
).append(deverSuggest).append(
$("<div class='wiktku-dfn-dever-input-row'></div>").append(deverInput).append(deverAddBtn)
);
deverChips.append(deverToggle);
deverWrap.append(deverChips).append(deverPanel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(deverWrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse CSS).
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-etiket", label: "Etîket", rows: 1, narrow: true, izah: "xisûsen, Mêrdîn, anatomî, Badînî", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-kategori", label: "Kategorî", rows: 1, narrow: true, izah: "wekî etîket lê xuya nabe tenê kategorîze dike", noLangSuffix: true, adv: true },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Etîket, Kategorî, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve chips aren't textareas, so readList() doesn't
// reach them — each chip's value lives in data-value.
function readDeverList() {
var vals = [];
block.find(".wiktku-dfn-dever-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readList("wiktku-dfn-etiket"),
kategori: readList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readDeverList()
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-etiket": sense.etiket,
"wiktku-dfn-kategori": sense.kategori,
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve chips — not a textarea sub-list, restored via the
// adder buildSenseBlock stashed on the block.
var addDeverChip = block.data("addDeverChip");
if (addDeverChip && sense.dever && sense.dever.length) {
$.each(sense.dever, function (_, v) { addDeverChip(v); });
}
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
tpqaqdndx8yu7ejlwz9jnbeog9dsq0g
6334122
6334119
2026-08-16T15:50:43Z
Wikihez
7344
6334122
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* Returns { wrap, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Tomar</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves the whole widget — same pattern
// as the ⊕ modifier picker above.
wrap.on("focusout", function () {
setTimeout(function () {
if (!$.contains(wrap[0], document.activeElement)) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow).append(panel);
return {
wrap: wrap,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices).
choices: [],
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
// posExamples loaded from [[MediaWiki:Gadget-start.js/data.json]].
var examples = steps.dfn.posExamples || {};
var full = values.pos || "";
return examples[full] || "";
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Mane…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
block.append(dfnArea);
block.append(nonKuWrap);
block.append(deverField.wrap);
block.append(etiketField.wrap);
block.append(kategoriField.wrap);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
block.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
block.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — per sense, after Dijmane ───────
// One toggle per sense rather than one for the whole step:
// the fields behind it (Formên alternatîv, Jornav…) belong to
// a single meaning, so a global switch flipped them for every
// sense at once and gave a count that answered "across all
// senses" — not a question anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
var advRow = $("<div class='wiktku-adv-toggle-row wiktku-sense-adv-row'></div>");
var advToggle = $("<a href='#' class='wiktku-adv-toggle'></a>").attr("aria-expanded", "false");
var advLabel = $("<span class='wiktku-adv-toggle-text'></span>");
advToggle.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>")).append(advLabel);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
advLabel.text(hidden ? "Malûmatên bikaranînê nîşan bide" : "Malûmatên bikaranînê veşêre");
advToggle.attr("aria-expanded", hidden ? "false" : "true")
.toggleClass("wiktku-adv-open", !hidden);
}
advToggle.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advRow.append(advToggle);
// After the last always-visible group (Dijmane) and before
// the advanced ones, which sit below it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advRow.insertAfter(lastPrimary);
else block.append(advRow);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, posExamples, jederDefs).
tplDefs: {},
posExamples: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState).
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
stepDiv.append($(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText)));
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.dfn.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
r7d2nw5z07p0hojvcnm36d0l7cgmevb
6334125
6334122
2026-08-16T16:25:44Z
Wikihez
7344
6334125
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* Returns { wrap, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Tomar</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves the whole widget — same pattern
// as the ⊕ modifier picker above.
wrap.on("focusout", function () {
setTimeout(function () {
if (!$.contains(wrap[0], document.activeElement)) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow).append(panel);
return {
wrap: wrap,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for: gender for nouns, verb
// type for verbs, a generic fallback otherwise.
question_fmt: function (values) {
var pos = values.pos || "";
// Cinsiyet/Jimar fields below are self-labelled, so no title needed here.
if (pos === "navdêr" || pos === "serenav") return "";
if (/^lêker/.test(pos)) return "Cureyê lêkerê";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
helpers.append(makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…"));
helpers.append(makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…"));
helpers.append(makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>/* */
2qy3hswwk2353g1ztbqc656x4ehd3xy
6334133
6334125
2026-08-16T17:15:10Z
Wikihez
7344
6334133
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* Returns { wrap, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Tomar</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves the whole widget — same pattern
// as the ⊕ modifier picker above.
wrap.on("focusout", function () {
setTimeout(function () {
if (!$.contains(wrap[0], document.activeElement)) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow).append(panel);
return {
wrap: wrap,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case.
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>sade (asayî)</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
helpers.append(makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…"));
helpers.append(makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…"));
helpers.append(makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
// Only ask for the structural form once gerguhêz/negerguhêz is answered.
stepDiv.find("#wiktku-posparams-row-lekturform")
.toggle(isLeker && !!stepDiv.find(".wiktku-posparams-lektur-sel").val());
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
1971zf5xk6vvza5nkt3gat2o8vvcnr4
6334152
6334133
2026-08-16T17:35:11Z
Wikihez
7344
6334152
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Mane zêde bike" / "+ Beş zêde bike", single button acting on "the one you're looking at" has
// only one thing it can possibly delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* Returns { wrap, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Tomar</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves the whole widget — same pattern
// as the ⊕ modifier picker above.
wrap.on("focusout", function () {
setTimeout(function () {
if (!$.contains(wrap[0], document.activeElement)) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow).append(panel);
return {
wrap: wrap,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
helpers.append(makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…"));
helpers.append(makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…"));
helpers.append(makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Mane zêde bike" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din zêde bike"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beş zêde bike"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din zêde bike",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "Beş zêde bike" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
f4962zeybybv34xp6axkb7jadu1aj77
6334169
6334152
2026-08-16T18:00:02Z
Wikihez
7344
6334169
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
function makeTrashButton(title) {
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Returns { wrap, panel, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
helpers.append(makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…"));
helpers.append(makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…"));
helpers.append(makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// "oojs-ui.styles.icons-moderation" carries the "trash" glyph used by
// makeTrashButton() below — without it the delete buttons on the Mane
// and Beş tab bars would render as blank icon-less buttons.
mw.loader.using(["oojs-ui-core", "oojs-ui.styles.icons-moderation"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
k6i1qkljosjkadi5i3bvugblw32fxab
6334179
6334169
2026-08-16T18:17:18Z
Wikihez
7344
6334179
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Returns { wrap, panel, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
helpers.append(makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…"));
helpers.append(makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…"));
helpers.append(makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
qo8sfyh6otmkvnukt4bhos9ib0i9lno
6334191
6334179
2026-08-16T19:05:23Z
Wikihez
7344
6334191
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Returns { wrap, panel, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez".
fnihaRow.find(".wiktku-posparams-field")
.before($("<span class='wiktku-posparams-pronoun-hint' id='wiktku-posparams-fniha-hint'></span>").text("ez").hide());
fborîRow.find(".wiktku-posparams-field")
.before($("<span class='wiktku-posparams-pronoun-hint' id='wiktku-posparams-fborî-hint'></span>").hide());
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
hyb2apbhl3ul828zieafp2fqg0yygf6
6334192
6334191
2026-08-16T19:10:29Z
Wikihez
7344
6334192
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Returns { wrap, panel, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) renderSuggest();
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez".
fnihaRow.find(".wiktku-posparams-field")
.before($("<span class='wiktku-posparams-pronoun-hint' id='wiktku-posparams-fniha-hint'></span>").text("ez").hide());
fborîRow.find(".wiktku-posparams-field")
.before($("<span class='wiktku-posparams-pronoun-hint' id='wiktku-posparams-fborî-hint'></span>").hide());
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
ie7tnmcwnhcpfoewsitcy857yuq59vr
6334196
6334192
2026-08-16T19:15:05Z
Wikihez
7344
6334196
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Returns { wrap, panel, addChip, values, setOpenDefault }.
*/
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(opts.suggestions || [], function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
renderSuggest();
panel.show();
input.focus();
});
function commitInput() { addChip(input.val()); input.val("").focus(); }
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) renderSuggest();
panel.toggle(!!open);
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî"
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike"
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
galga2xfj5bp6jjdvyi5dqwovt1f521
6334198
6334196
2026-08-16T19:25:22Z
Wikihez
7344
6334198
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
remove.on("click", function () { chip.remove(); renderSuggest(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
olwj5y6z895b952esok4thb1ze13m11
6334202
6334198
2026-08-16T19:53:05Z
Wikihez
7344
6334202
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
function makeFieldHelp(text) {
return $("<span class='wiktku-field-help' tabindex='0'>?</span>").attr("title", text);
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-help")
.attr("title", step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
rn1b41h3r8c02649licmefaww29q5wm
6334213
6334202
2026-08-16T20:22:32Z
Wikihez
7344
6334213
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
// A small "?" info icon with a native hover/focus tooltip — the
// lightweight per-field help affordance from the redesign spec ("Entry
// Wizard Redesign Spec.dc.html"). Deliberately just a title attribute
// rather than a click-to-open panel — that's the template ⓘ's job
// (see showTemplateInfo), this is a quicker one-line hint.
// A "?" that opens a small popover on click/tap/Enter (title-attribute
// tooltips don't respond to touch, so mobile got no way to see this at
// all). Closes on a second click, Escape, or a click outside it.
function makeFieldHelp(text) {
var icon = $("<span class='wiktku-field-help' role='button' tabindex='0' aria-expanded='false'>?</span>");
var popover = $("<div class='wiktku-field-help-popover'></div>").hide();
var wrap = $("<span class='wiktku-field-help-wrap'></span>").append(icon).append(popover);
function setOpen(open) {
popover.toggle(!!open);
icon.attr("aria-expanded", open ? "true" : "false");
}
icon.on("click", function (e) {
e.stopPropagation();
setOpen(!popover.is(":visible"));
});
icon.on("keydown", function (e) {
if (e.which === 13 || e.which === 32) { e.preventDefault(); setOpen(!popover.is(":visible")); }
else if (e.which === 27) { setOpen(false); }
});
$(document).on("click", function (e) {
if (popover.is(":visible") && !$.contains(wrap[0], e.target)) setOpen(false);
});
// Exposed so refresh() can update the text of a step's help without
// tearing down and rebuilding the whole widget (see step.help).
wrap.data("wiktkuSetHelpText", function (t) { popover.text(t || ""); });
popover.text(text || "");
return wrap;
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Makes more sense as this question's own "?" tooltip instead.
posExamples: {},
help: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append(makeFieldHelp("Hevokek an mînakek ku nîşan dide ev kelîme çawa tê bikaranîn."));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ")
.append(makeFieldHelp("Jêgirtineke rasterast ji kitêb, gotar an çavkaniyeke din, bi çavkaniya wê ve."));
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
legend.append(makeFieldHelp("Cudahiyên nivîsandinê, termên nêzîk (koordîne), û peyvên jor/jêr ên vê maneyê — hemû ne mecbûrî ne."));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
help: "Kurmancî bi alfabeyên din jî tê nivîsandin (Erebî, Kîrîlî, hwd) — ji ber vê yekê ev pirs tenê ji bo kurmancî, zaravayên din ên kurdî, an zimanên ku bi zêdetirî ji yek alfabeyê tên nivîsandin xuya dibe.",
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
// Same for its "?" tooltip, if it has one (e.g. pos's, which explains
// whichever cureyê rêzimanî is currently chosen — see steps.pos.help).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
if (typeof step.help === "function") {
var helpEl = $("#wiktku-start-" + stepId + " .wiktku-start-step-help");
var setHelpText = helpEl.data("wiktkuSetHelpText");
if (setHelpText) setHelpText(step.help(values));
}
});
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
// Optional "?" tooltip next to the question — same shape as
// question_fmt (string or function of values), kept in sync
// by refresh() via the .wiktku-start-step-help class. The
// question label is block-level (its own line), so with a
// tooltip the two go in a row together instead.
if (step.help) {
var helpText = typeof step.help === "function" ? step.help(getValues()) : step.help;
stepDiv.append(
$("<div class='wiktku-start-step-question-row'></div>")
.append(questionLabel)
.append(makeFieldHelp(helpText).addClass("wiktku-start-step-help"))
);
} else {
stepDiv.append(questionLabel);
}
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
r4jtin3okhaltg5uwlgmqph9fsay44c
6334221
6334213
2026-08-16T20:40:08Z
Wikihez
7344
6334221
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{devok|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no devok template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{devok|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{devok|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
question_br: true,
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
question_br: true,
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
question_br: true,
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — second dropdown next to Cinsiyet, writes
// into the shared hidden #wiktku-start-jimar-field, into its
// own j= param instead of sharing c=. Options + tooltip are
// built in syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(jimarRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema bilanî (fsubj)", "Forma dema bilanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema bilanî", "Transliterasyona forma dema bilanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema bilanî (subj)", "Rehê dema bilanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema bilanî", "Transliterasyona rehê dema bilanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{devok|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
if (step.question_br) stepDiv.append($("<br>"));
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
b67f9fex4rtprkhhlx1s52i6b2kj83a
6334231
6334221
2026-08-16T20:56:16Z
Wikihez
7344
6334231
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, not a step before it — so the box comes first
// and this sits under it as a quiet link. It only expands
// for the minority of senses that are a pointer at another
// word (guharto, şaşnivîs, kevnbûyî) rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
block.append(tplLink);
block.append(tplRow);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
9gbabkjto3d4aqdjurv5mhbo00mpq3z
6334234
6334231
2026-08-16T21:01:19Z
Wikihez
7344
6334234
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
4n6dvq0nyurqb1ddkacbqqgigie91w9
6334239
6334234
2026-08-16T21:21:11Z
Wikihez
7344
6334239
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fniha/fborî: the actual conjugated forms (as opposed
// to niha/borî/subj below, which are the bare roots the Lua
// module conjugates from) — primary fields for lêker, not
// tucked behind "Tercîhên pêşketî", same reasoning as tr above.
// Always shown for lêker — see the isLeker toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// Always shown for lêker, regardless of language — unlike
// subj/subjtr above, these aren't skipped for ku.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLeker && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
var isKuLeker = isLeker && lang === "ku";
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên bikaranînê". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Persistent 🔗/i pair for the chosen template — the dropdown
// only shows these while searching, and once a template is
// picked there is otherwise no way back to what it does or
// its own page. Reuses the same icon markup/classes as the
// dropdown row (see markTemplateOptions/showTemplateInfo).
var tplSelIcons = $("<span class='wiktku-tpl-icons wiktku-dfn-tpl-sel-icons'></span>").hide();
var tplSelLink = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke");
var tplSelInfo = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
tplSelInfo.on("click", function (e) {
e.preventDefault();
var name = tplSelect.val();
if (name) showTemplateInfo(name, tplSelIcons);
});
tplSelIcons.append(tplSelLink).append(tplSelInfo);
/** Shows/hides the pair and points the link at whichever
* template is currently chosen. Called from tplSelect's own
* "change" handler, and again on restore since .val() alone
* doesn't fire it. */
function updateTplSelIcons() {
var name = tplSelect.val();
if (name) {
tplSelLink.attr("href", wiktkuUtil.tplPageUrl(name));
tplSelIcons.show();
} else {
tplSelIcons.hide();
}
}
block.data("updateTplSelIcons", updateTplSelIcons);
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either way.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateTplSelIcons();
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(tplSelIcons).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên bikaranînê" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên bikaranînê" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse ku otomatîk were tijekirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên bikaranînê" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên bikaranînê</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
// One reassurance, shown once for the whole Mane step: the
// "an jî şablonek hilbijêre" dropdown inside each sense is
// optional — plain text is the normal path.
if (!stepDiv.find(".wiktku-dfn-tpl-hint").length) {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-dfn-tpl-hint'></p>").text(
"Ji bo her maneyekê, dropdown-a \"an jî şablonek hilbijêre\" ne mecbûrî ye — bi gelemperî tu tenê maneyê bi tîpên asayî dinivîsî. Ew şablon tenê ji bo rewşên taybet in (mînak: guherto, kevnbûyî, şaşnivîs); heke tu nizanî kîjan divê, vale bihêle."
)
);
}
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Same reason — the 🔗/i pair also depends on tplSelect's value.
var updateTplIcons = block.data("updateTplSelIcons");
if (updateTplIcons) updateTplIcons();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên bikaranînê" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
t5g7z6ll193lztkyokba3bvv2a932wc
6334245
6334239
2026-08-16T21:59:05Z
Wikihez
7344
6334245
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fboriRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbeRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fboriRow, "wiktku-posparams-fbori-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fboriRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbeRow);
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead, and never needs a translit row since
// ku is already Latin script.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fbori").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fboritr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbe").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fbori's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fbori-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Persistent 🔗/i pair for the chosen template — the dropdown
// only shows these while searching, and once a template is
// picked there is otherwise no way back to what it does or
// its own page. Reuses the same icon markup/classes as the
// dropdown row (see markTemplateOptions/showTemplateInfo).
var tplSelIcons = $("<span class='wiktku-tpl-icons wiktku-dfn-tpl-sel-icons'></span>").hide();
var tplSelLink = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke");
var tplSelInfo = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
tplSelInfo.on("click", function (e) {
e.preventDefault();
var name = tplSelect.val();
if (name) showTemplateInfo(name, tplSelIcons);
});
tplSelIcons.append(tplSelLink).append(tplSelInfo);
/** Shows/hides the pair and points the link at whichever
* template is currently chosen. Called from tplSelect's own
* "change" handler, and again on restore since .val() alone
* doesn't fire it. */
function updateTplSelIcons() {
var name = tplSelect.val();
if (name) {
tplSelLink.attr("href", wiktkuUtil.tplPageUrl(name));
tplSelIcons.show();
} else {
tplSelIcons.hide();
}
}
block.data("updateTplSelIcons", updateTplSelIcons);
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateTplSelIcons();
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(tplSelIcons).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Same reason — the 🔗/i pair also depends on tplSelect's value.
var updateTplIcons = block.data("updateTplSelIcons");
if (updateTplIcons) updateTplIcons();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
m9mkokazqxdaieyf7whfvu7in3r8c7f
6334246
6334245
2026-08-16T22:00:56Z
Wikihez
7344
6334246
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with choices built from
// whichever of metadata[lang].g are number codes (see isNumberCode).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as ONE question — this name is
// only a no-CSS/no-JS fallback, since the h3 that would show it
// is hidden by CSS.
name: "taybetmendî",
// Shows what's actually being asked for, when the fields below
// aren't already self-labelled — a generic fallback otherwise.
// "Cureyê lêkerê" used to show for lêker, but that just repeats
// what "Cureyê rêzimanî" already said one card up (see the pos
// step above, now fused flush against this one — no gap, no
// duplicate heading).
question_fmt: function (values) {
var pos = values.pos || "";
if (pos === "navdêr" || pos === "serenav" || /^lêker/.test(pos)) return "";
return "Taybetmendî";
},
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender AND number codes together —
// split them here (see isNumberCode) so Cîns only ever offers
// gender codes (and gender+plural compounds like m-p/n-p,
// which aren't NUMBER_LABELS keys) and Jimar only offers number codes.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = $.grep(allCodes, function (c) { return isNumberCode(c); });
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead, and never needs a translit row since
// ku is already Latin script.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Persistent 🔗/i pair for the chosen template — the dropdown
// only shows these while searching, and once a template is
// picked there is otherwise no way back to what it does or
// its own page. Reuses the same icon markup/classes as the
// dropdown row (see markTemplateOptions/showTemplateInfo).
var tplSelIcons = $("<span class='wiktku-tpl-icons wiktku-dfn-tpl-sel-icons'></span>").hide();
var tplSelLink = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke");
var tplSelInfo = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
tplSelInfo.on("click", function (e) {
e.preventDefault();
var name = tplSelect.val();
if (name) showTemplateInfo(name, tplSelIcons);
});
tplSelIcons.append(tplSelLink).append(tplSelInfo);
/** Shows/hides the pair and points the link at whichever
* template is currently chosen. Called from tplSelect's own
* "change" handler, and again on restore since .val() alone
* doesn't fire it. */
function updateTplSelIcons() {
var name = tplSelect.val();
if (name) {
tplSelLink.attr("href", wiktkuUtil.tplPageUrl(name));
tplSelIcons.show();
} else {
tplSelIcons.hide();
}
}
block.data("updateTplSelIcons", updateTplSelIcons);
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateTplSelIcons();
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(tplSelIcons).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Same reason — the 🔗/i pair also depends on tplSelect's value.
var updateTplIcons = block.data("updateTplSelIcons");
if (updateTplIcons) updateTplIcons();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
2dszthuuebqo1ejk6pb3x0y4mngr6bh
6334261
6334246
2026-08-16T22:45:37Z
Wikihez
7344
6334261
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
li.append(ta).append(btn).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". Wrapped together
// with the box in its own flex line (.wiktku-posparams-field-
// line) so the hint sits right before the box even when the
// row itself stacks label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez".
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Persistent 🔗/i pair for the chosen template — the dropdown
// only shows these while searching, and once a template is
// picked there is otherwise no way back to what it does or
// its own page. Reuses the same icon markup/classes as the
// dropdown row (see markTemplateOptions/showTemplateInfo).
var tplSelIcons = $("<span class='wiktku-tpl-icons wiktku-dfn-tpl-sel-icons'></span>").hide();
var tplSelLink = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke");
var tplSelInfo = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
tplSelInfo.on("click", function (e) {
e.preventDefault();
var name = tplSelect.val();
if (name) showTemplateInfo(name, tplSelIcons);
});
tplSelIcons.append(tplSelLink).append(tplSelInfo);
/** Shows/hides the pair and points the link at whichever
* template is currently chosen. Called from tplSelect's own
* "change" handler, and again on restore since .val() alone
* doesn't fire it. */
function updateTplSelIcons() {
var name = tplSelect.val();
if (name) {
tplSelLink.attr("href", wiktkuUtil.tplPageUrl(name));
tplSelIcons.show();
} else {
tplSelIcons.hide();
}
}
block.data("updateTplSelIcons", updateTplSelIcons);
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateTplSelIcons();
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(tplSelIcons).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Ev mane li ku derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
$.each(def.fields, function (key, label) {
var row = $("<div></div>").addClass("wiktku-dfn-quote-src-row");
var fieldLbl = $("<label></label>").text(label + ": ");
var inp = $("<textarea></textarea>")
.attr("rows", 2)
.addClass("wiktku-dfn-quote-src-input wiktku-start-narrow")
.attr("data-field-key", key)
.attr("placeholder", label + "…");
// "Sernav" / "Sernavê orjînal" (title fields, e.g. for
// kitêb/kovar/malper) get a taller, draggable box; every
// other field in the template stays as-is. Field labels
// come from the on-wiki jederDefs data, so match on the
// (diacritic-folded) label text rather than a fixed key.
var foldedLabel = foldDiacritics(label);
if (/^sernav$/.test(foldedLabel) || /orjin/.test(foldedLabel)) {
inp.addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4)
.attr("placeholder", "{{ku-tewîn-nav|nêr}}");
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
setTimeout(function () {
autoPopulate();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
autoPopulate);
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// bnr-h — {{bnr-h|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "binêre herwiha (bnr-h)",
question_fmt: "Şablona bnr-h li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{bnr-h|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "\n* {{" + name + "}}");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— beşek hilbijêre —");
sel.append($("<option value=''>— beşek hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Same reason — the 🔗/i pair also depends on tplSelect's value.
var updateTplIcons = block.data("updateTplSelIcons");
if (updateTplIcons) updateTplIcons();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// bnr-h goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── bnr-h merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{bnr-h|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{bnr-h\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{bnr-h\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{bnr-h|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
loq24wqmsy9gnzn6h2cxzehg1kwrx2w
6334273
6334261
2026-08-16T23:35:26Z
Wikihez
7344
6334273
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* Once the word is typed and the field is left (blur), it collapses into
* a compact pill so the list reads as a row of words rather than a stack
* of open boxes; clicking the pill re-opens it (textarea + ⊕) for editing.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var pillText = $("<span class='wiktku-word-pill-text'></span>");
var pillRemove = $("<button type='button' class='wiktku-word-pill-remove' title='Rake'>×</button>");
var pill = $("<span class='wiktku-word-pill' role='button' tabindex='0'></span>")
.append(pillText).append(pillRemove).hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// Close picker when focus leaves the li entirely.
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
/** Collapses this row into a pill — a no-op while it's still empty
* (the list's trailing "type here" row stays open) or already
* collapsed. Public via li.data("collapse") for restoreSubList. */
function collapse() {
var val = $.trim(ta.val());
if (!val) return;
pillText.text(val);
ta.hide();
btn.hide();
picker.hide();
li.addClass("wiktku-word-li--pilled");
pill.show();
}
/** Reopens the row for editing — used when the pill itself is clicked. */
function expand() {
li.removeClass("wiktku-word-li--pilled");
pill.hide();
ta.show();
btn.show();
ta.focus();
}
li.data("collapse", collapse);
// Only collapse once the person is actually done with the word (blur)
// and it's no longer the list's last/still-growing row — collapsing
// on every keystroke would turn the word into a pill mid-typing.
ta.on("blur", function () {
setTimeout(function () {
if (!li.is(li.parent().children(":last-child"))) collapse();
}, 150);
});
// Enter commits the word the same way tabbing away does, instead of
// inserting a newline into what's meant to be a one-line field.
ta.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); ta[0].blur(); }
});
pill.on("click", function (e) {
if ($(e.target).is(pillRemove)) return;
expand();
});
pill.on("keydown", function (e) {
if (e.which === 13 || e.which === 32) { e.preventDefault(); expand(); }
});
pillRemove.on("click", function (e) {
e.stopPropagation();
li.remove();
});
li.append(ta).append(btn).append(picker).append(pill);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list" +
(useModifiers ? " wiktku-dfn-sublist--pills" : ""));
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same wrapping pill row as the per-sense ⊕-modifier groups
// (see wiktku-dfn-sublist--pills) — words collapse into pills
// once typed, instead of stacking one full-width box each.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.addClass(step.modifiers ? "wiktku-word-list--pills" : "")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
0y7jtavkbkrp01vjwy6zarowp94ife0
6334280
6334273
2026-08-16T23:51:42Z
Wikihez
7344
6334280
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Short parenthetical prefix each modifier key gets in a pill's friendly
// display — e.g. "peyv<t:wate>" reads as "peyv (wate)", "peyv<c:mê>" as
// "peyv (cinsiyet: mê)". Keyed the same as WORD_MODIFIERS.
var WORD_MODIFIER_DISPLAY_PREFIX = {
t: "", // translation/gloss reads fine bare: "word (gloss)"
cuda: "cuda: ",
c: "cinsiyet: ",
e: "etîket: ",
ee: "etîket: ",
q: "wesf: ",
qq: "wesf: ",
crz: "cureyê rêzimanî: ",
hrf: "herfiyen: ",
ref: "çavkanî: "
};
/** Turns a raw "word<t:val1><c:val2>" field value into a readable
* "word (val1; cinsiyet: val2)" label for a pill — the raw <key:value>
* markup underneath (what's actually saved/output) is untouched. */
function prettifyWordValue(raw) {
var m = /^([\s\S]*?)((?:<[a-zA-Z]+:[^<>]*>)+)$/.exec(raw || "");
if (!m) return raw || "";
var base = m[1];
var parts = [];
var re = /<([a-zA-Z]+):([^<>]*)>/g, mm;
while ((mm = re.exec(m[2])) !== null) {
var prefix = WORD_MODIFIER_DISPLAY_PREFIX[mm[1]];
parts.push((prefix === undefined ? mm[1] + ": " : prefix) + mm[2]);
}
return parts.length ? base + " (" + parts.join("; ") + ")" : base;
}
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* Once the word is typed and the field is left (blur), it collapses into
* a compact pill so the list reads as a row of words rather than a stack
* of open boxes; clicking the pill re-opens it (textarea + ⊕) for editing.
* `ta` — the jQuery textarea element.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var pillText = $("<span class='wiktku-word-pill-text'></span>");
var pillRemove = $("<button type='button' class='wiktku-word-pill-remove' title='Rake'>×</button>");
// Named wordPill (not "pill") — buildPills() below has its own local
// "pill" per ⊕ picker option, a different, unrelated element.
var wordPill = $("<span class='wiktku-word-pill' role='button' tabindex='0'></span>")
.append(pillText).append(pillRemove).hide();
// Which modifier keys are already present in the textarea's current
// text — e.g. "<t:...>" → "t" used. Each modifier can only be
// applied once per word, so its pill gets disabled once used.
function usedKeys() {
var used = {};
var re = /<([a-zA-Z]+):/g;
var val = ta.val() || "";
var m;
while ((m = re.exec(val)) !== null) used[m[1]] = true;
return used;
}
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
ta.val(ta.val() + "<" + mod.key + ":" + v + ">");
ta.trigger("input");
}
valWrap.remove();
ta.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); ta.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); ta.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since the text (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// ⊕ only makes sense once there's a word to attach the modifier to —
// on the empty trailing "type here" row it would otherwise produce a
// pill that's nothing but bare <key:value> markup with no word in it.
function updateBtnVisibility() {
var hasWord = !!$.trim(ta.val());
btn.toggle(hasWord);
if (!hasWord) picker.hide();
}
updateBtnVisibility();
ta.on("input", updateBtnVisibility);
/** Collapses this row into a pill — a no-op while it's still empty
* (the list's trailing "type here" row stays open) or already
* collapsed. Public via li.data("collapse") for restoreSubList. */
function collapse() {
var val = $.trim(ta.val());
if (!val) return;
pillText.text(prettifyWordValue(val));
ta.hide();
btn.hide();
picker.hide();
li.addClass("wiktku-word-li--pilled");
wordPill.show();
}
/** Reopens the row for editing — used when the pill itself is clicked. */
function expand() {
li.removeClass("wiktku-word-li--pilled");
wordPill.hide();
ta.show();
updateBtnVisibility();
ta.focus();
}
li.data("collapse", collapse);
// Closes the picker and, once it's no longer the list's last/still-
// growing row, collapses the word into a pill — but only once focus
// has left the li ENTIRELY, not just the textarea. Clicking ⊕ (or the
// modifier picker's own value input) blurs ta too, and that must NOT
// collapse the row out from under the picker it just opened.
li.on("focusout", function () {
setTimeout(function () {
if ($.contains(li[0], document.activeElement)) return;
picker.hide();
if (!li.is(li.parent().children(":last-child"))) collapse();
}, 150);
});
// Enter commits the word the same way tabbing away does, instead of
// inserting a newline into what's meant to be a one-line field.
ta.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); ta[0].blur(); }
});
wordPill.on("click", function (e) {
if ($(e.target).is(pillRemove)) return;
expand();
});
wordPill.on("keydown", function (e) {
if (e.which === 13 || e.which === 32) { e.preventDefault(); expand(); }
});
pillRemove.on("click", function (e) {
e.stopPropagation();
li.remove();
});
li.append(ta).append(btn).append(picker).append(wordPill);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list" +
(useModifiers ? " wiktku-dfn-sublist--pills" : ""));
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
items.eq(j).find("textarea").val(val);
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same wrapping pill row as the per-sense ⊕-modifier groups
// (see wiktku-dfn-sublist--pills) — words collapse into pills
// once typed, instead of stacking one full-width box each.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.addClass(step.modifiers ? "wiktku-word-list--pills" : "")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
8jefc6qs8vy7h6kk6hftah4nuh0l9qb
6334288
6334280
2026-08-17T08:18:15Z
Wikihez
7344
6334288
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Short parenthetical prefix each modifier key gets in a pill's friendly
// display — e.g. "peyv<t:wate>" reads as "peyv (wate)", "peyv<c:mê>" as
// "peyv (cinsiyet: mê)". Keyed the same as WORD_MODIFIERS.
var WORD_MODIFIER_DISPLAY_PREFIX = {
t: "", // translation/gloss reads fine bare: "word (gloss)"
cuda: "cuda: ",
c: "cinsiyet: ",
e: "etîket: ",
ee: "etîket: ",
q: "wesf: ",
qq: "wesf: ",
crz: "cureyê rêzimanî: ",
hrf: "herfiyen: ",
ref: "çavkanî: "
};
/** Splits a raw "word<t:val1><c:val2>" field value into { word, mods }
* — mods is an array of {key, value} in the order they appear. Used to
* re-hydrate a word box's UI (plain word + separate modifier chips)
* from a value that was typed/restored in the combined raw form. */
function parseWordValue(raw) {
var m = /^([\s\S]*?)((?:<[a-zA-Z]+:[^<>]*>)+)$/.exec(raw || "");
if (!m) return { word: raw || "", mods: [] };
var mods = [];
var re = /<([a-zA-Z]+):([^<>]*)>/g, mm;
while ((mm = re.exec(m[2])) !== null) mods.push({ key: mm[1], value: mm[2] });
return { word: m[1], mods: mods };
}
/** Re-joins { word, mods } back into the raw "word<t:val1><c:val2>" form
* that readList()/output actually read. */
function joinWordValue(word, mods) {
if (!word) return "";
var out = word;
$.each(mods, function (_, mo) { out += "<" + mo.key + ":" + mo.value + ">"; });
return out;
}
/** Short "(key: value)" chip label for one attached modifier. */
function modChipLabel(mo) {
var prefix = WORD_MODIFIER_DISPLAY_PREFIX[mo.key];
return (prefix === undefined ? mo.key + ": " : prefix) + mo.value;
}
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` stays the hidden "source of truth" element — its value is always
* the raw "word<key:value>…" text that readList()/restoreSubList()/
* output already read and write, and the caller's own growth listener
* (onSubInput/refreshForTextField, bound to `ta`'s "input" event before
* this runs) keeps working untouched. Everything the person actually
* sees and types into is separate: a plain word box (wordInput, holds
* only the word — modifier tags can never be typed/corrupted through
* it) plus a row of small removable chips, one per attached modifier,
* shown below it. Once the word is typed and the box is left (blur), it
* collapses into a compact pill with its modifier chips (if any) shown
* below the pill; clicking the pill re-opens the word box for editing —
* still only the word, never the tags.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var narrow = (ta.attr("class") || "").split(/\s+/).indexOf("wiktku-start-narrow") !== -1;
// Deliberately NOT ta's own classes (wiktku-dfn-subfield/wiktku-
// start-step-field) — a lot of code elsewhere counts/indexes
// elements by those exact classes (restoreMultiField, readList's
// siblings, refresh()'s per-step field lookups, …); a second
// visible textarea matching them would throw those counts off.
// wiktku-word-input picks up the same look via styles.css instead.
var wordInput = $("<textarea></textarea>")
.attr("rows", ta.attr("rows") || 1)
.addClass("wiktku-word-input" + (narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", ta.attr("placeholder") || "");
ta.hide();
// Placeholders on the (hidden) canonical field are set by shared
// code like updateSubPlaceholders() that has no reason to know
// wordInput exists — mirror them across automatically instead of
// touching every one of those call sites.
if (window.MutationObserver) {
new MutationObserver(function () {
wordInput.attr("placeholder", ta.attr("placeholder") || "");
}).observe(ta[0], { attributes: true, attributeFilter: ["placeholder"] });
}
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var modsRow = $("<div class='wiktku-word-mods'></div>").hide();
var pillText = $("<span class='wiktku-word-pill-text'></span>");
var pillRemove = $("<button type='button' class='wiktku-word-pill-remove' title='Rake'>×</button>");
// Named wordPill (not "pill") — buildPills() below has its own local
// "pill" per ⊕ picker option, a different, unrelated element.
var wordPill = $("<span class='wiktku-word-pill' role='button' tabindex='0'></span>")
.append(pillText).append(pillRemove).hide();
// The word text and its modifiers are tracked separately — mods is
// never part of what wordInput holds, so typing into the word box
// can only ever change the word, never a tag. Parsed once from
// whatever ta already holds (e.g. a value restored from cache).
var parsed = parseWordValue(ta.val());
wordInput.val(parsed.word);
var mods = parsed.mods;
function usedKeys() {
var used = {};
$.each(mods, function (_, mo) { used[mo.key] = true; });
return used;
}
/** Writes ta's value from the current word+mods and tells the
* caller's growth logic (bound to ta's "input" event) about it. */
function syncTa() {
ta.val(joinWordValue($.trim(wordInput.val()), mods));
ta.trigger("input");
}
function renderMods() {
modsRow.empty();
$.each(mods, function (idx, mo) {
var chip = $("<span class='wiktku-word-mod-chip'></span>").text(modChipLabel(mo));
var x = $("<button type='button' class='wiktku-word-mod-chip-remove' title='Vê taybetmendiyê rake'>×</button>");
x.on("click", function (e) {
e.stopPropagation();
mods.splice(idx, 1);
syncTa();
renderMods();
});
chip.append(x);
modsRow.append(chip);
});
modsRow.toggle(!!mods.length);
// A word with modifiers always takes the whole row in the outer
// list — otherwise its chip line, being wider than the pill
// itself, would visually run on under a neighboring word's pill
// instead of reading as clearly attached to this one.
li.toggleClass("wiktku-word-li--has-mods", !!mods.length);
}
renderMods();
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
picker.hide();
// Show a tiny inline input for the modifier value.
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function applyModifier() {
var v = $.trim(valInp.val());
if (v) {
mods.push({ key: mod.key, value: v });
syncTa();
renderMods();
}
valWrap.remove();
wordInput.focus();
}
confirm.on("click", applyModifier);
cancel.on("click", function () { valWrap.remove(); wordInput.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); applyModifier(); }
if (e.which === 27) { valWrap.remove(); wordInput.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since mods (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// ⊕ only makes sense once there's a word to attach the modifier to —
// on the empty trailing "type here" box it would otherwise produce a
// modifier with no word to attach it to.
function updateBtnVisibility() {
var hasWord = !!$.trim(wordInput.val());
btn.toggle(hasWord);
if (!hasWord) picker.hide();
}
updateBtnVisibility();
wordInput.on("input", function () {
updateBtnVisibility();
syncTa();
});
/** Collapses this row into a pill — a no-op while it's still empty
* (the list's trailing "type here" row stays open) or already
* collapsed. Public via li.data("collapse") for restoreSubList. */
function collapse() {
var word = $.trim(wordInput.val());
if (!word) return;
pillText.text(word);
wordInput.hide();
btn.hide();
picker.hide();
li.addClass("wiktku-word-li--pilled");
wordPill.show();
}
/** Reopens the word box for editing — used when the pill itself is
* clicked. Only the word becomes editable text again; mods are
* changed only via their own chip ×/the ⊕ picker, never by typing. */
function expand() {
li.removeClass("wiktku-word-li--pilled");
wordPill.hide();
wordInput.show();
updateBtnVisibility();
wordInput.focus();
}
li.data("collapse", collapse);
// Lets restoreSubList's "fill an existing row" path set this word's
// value correctly (re-split into word+mods) instead of dumping the
// raw "word<key:value>" string straight into wordInput.
li.data("setRawValue", function (raw) {
var p = parseWordValue(raw);
wordInput.val(p.word);
mods = p.mods;
syncTa();
renderMods();
updateBtnVisibility();
});
// Closes the picker and, once it's no longer the list's last/still-
// growing row, collapses the word into a pill — but only once focus
// has left the li ENTIRELY, not just the word box. Clicking ⊕ (or the
// modifier picker's own value input) blurs wordInput too, and that
// must NOT collapse the row out from under the picker it just opened.
li.on("focusout", function () {
setTimeout(function () {
if ($.contains(li[0], document.activeElement)) return;
picker.hide();
if (!li.is(li.parent().children(":last-child"))) collapse();
}, 150);
});
// Enter commits the word the same way tabbing away does, instead of
// inserting a newline into what's meant to be a one-line field.
wordInput.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); wordInput[0].blur(); }
});
wordPill.on("click", function (e) {
if ($(e.target).is(pillRemove)) return;
expand();
});
wordPill.on("keydown", function (e) {
if (e.which === 13 || e.which === 32) { e.preventDefault(); expand(); }
});
pillRemove.on("click", function (e) {
e.stopPropagation();
li.remove();
});
li.append(ta).append(wordInput).append(btn).append(picker).append(wordPill).append(modsRow);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list" +
(useModifiers ? " wiktku-dfn-sublist--pills" : ""));
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
// useModifiers rows keep the word and its modifier tags in
// separate elements (see makeModifierLi) — setRawValue()
// re-splits val() the same way parsing it fresh would,
// instead of dumping the raw "word<key:value>" string into
// the visible word box.
var setRaw = items.eq(j).data("setRawValue");
if (useModifiers && typeof setRaw === "function") {
setRaw(val);
} else {
items.eq(j).find("textarea").val(val);
}
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
if (!useModifiers) return;
// Every restored word collapses straight into a pill (the state
// typing + tabbing away would leave it in), and a fresh empty
// row goes on the end so there's always somewhere to add the
// next word without first having to click into a filled one.
var filled = subList.find("li.wiktku-word-li");
filled.each(function () {
var collapseFn = $(this).data("collapse");
if (typeof collapseFn === "function") collapseFn();
});
if (filled.last().is(":last-child")) {
var nextTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-next-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow");
nextTa.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(makeModifierLi(nextTa));
}
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same wrapping pill row as the per-sense ⊕-modifier groups
// (see wiktku-dfn-sublist--pills) — words collapse into pills
// once typed, instead of stacking one full-width box each.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.addClass(step.modifiers ? "wiktku-word-list--pills" : "")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
gb4tgqdiag8zjvvxv6sf01jj4x2c0sk
6334292
6334288
2026-08-17T08:39:47Z
Wikihez
7344
6334292
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Short parenthetical prefix each modifier key gets in a pill's friendly
// display — e.g. "peyv<t:wate>" reads as "peyv (wate)", "peyv<c:mê>" as
// "peyv (cinsiyet: mê)". Keyed the same as WORD_MODIFIERS.
var WORD_MODIFIER_DISPLAY_PREFIX = {
t: "", // translation/gloss reads fine bare: "word (gloss)"
cuda: "cuda: ",
c: "cinsiyet: ",
e: "etîket: ",
ee: "etîket: ",
q: "wesf: ",
qq: "wesf: ",
crz: "cureyê rêzimanî: ",
hrf: "herfiyen: ",
ref: "çavkanî: "
};
/** Splits a raw "word<t:val1><c:val2>" field value into { word, mods }
* — mods is an array of {key, value} in the order they appear. Used to
* re-hydrate a word box's UI (plain word + separate modifier chips)
* from a value that was typed/restored in the combined raw form. */
function parseWordValue(raw) {
var m = /^([\s\S]*?)((?:<[a-zA-Z]+:[^<>]*>)+)$/.exec(raw || "");
if (!m) return { word: raw || "", mods: [] };
var mods = [];
var re = /<([a-zA-Z]+):([^<>]*)>/g, mm;
while ((mm = re.exec(m[2])) !== null) mods.push({ key: mm[1], value: mm[2] });
return { word: m[1], mods: mods };
}
/** Re-joins { word, mods } back into the raw "word<t:val1><c:val2>" form
* that readList()/output actually read. */
function joinWordValue(word, mods) {
if (!word) return "";
var out = word;
$.each(mods, function (_, mo) { out += "<" + mo.key + ":" + mo.value + ">"; });
return out;
}
/** Short "(key: value)" chip label for one attached modifier. */
function modChipLabel(mo) {
var prefix = WORD_MODIFIER_DISPLAY_PREFIX[mo.key];
return (prefix === undefined ? mo.key + ": " : prefix) + mo.value;
}
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` stays the hidden "source of truth" element — its value is always
* the raw "word<key:value>…" text that readList()/restoreSubList()/
* output already read and write, and the caller's own growth listener
* (onSubInput/refreshForTextField, bound to `ta`'s "input" event before
* this runs) keeps working untouched. Everything the person actually
* sees and types into is separate: a plain word box (wordInput, holds
* only the word — modifier tags can never be typed/corrupted through
* it, and it never changes shape, so it always looks and behaves like
* any other text field) plus a row of small tag pills, one per attached
* modifier, attached right above that word's box. Clicking a tag pill
* (not its ×) reopens it for editing its value; the ⊕ button adds more.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var narrow = (ta.attr("class") || "").split(/\s+/).indexOf("wiktku-start-narrow") !== -1;
// Deliberately NOT ta's own classes (wiktku-dfn-subfield/wiktku-
// start-step-field) — a lot of code elsewhere counts/indexes
// elements by those exact classes (restoreMultiField, readList's
// siblings, refresh()'s per-step field lookups, …); a second
// visible textarea matching them would throw those counts off.
// wiktku-word-input picks up the same look via styles.css instead.
var wordInput = $("<textarea></textarea>")
.attr("rows", ta.attr("rows") || 1)
.addClass("wiktku-word-input" + (narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", ta.attr("placeholder") || "");
ta.hide();
// Placeholders on the (hidden) canonical field are set by shared
// code like updateSubPlaceholders() that has no reason to know
// wordInput exists — mirror them across automatically instead of
// touching every one of those call sites.
if (window.MutationObserver) {
new MutationObserver(function () {
wordInput.attr("placeholder", ta.attr("placeholder") || "");
}).observe(ta[0], { attributes: true, attributeFilter: ["placeholder"] });
}
var modsRow = $("<div class='wiktku-word-mods'></div>").hide();
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var removeBtn = $("<button type='button' class='wiktku-word-remove' title='Vê peyvê rake'>×</button>").hide();
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var boxRow = $("<div class='wiktku-word-box-row'></div>")
.append(wordInput).append(btn).append(removeBtn);
// The word text and its modifiers are tracked separately — mods is
// never part of what wordInput holds, so typing into the word box
// can only ever change the word, never a tag. Parsed once from
// whatever ta already holds (e.g. a value restored from cache).
var parsed = parseWordValue(ta.val());
wordInput.val(parsed.word);
var mods = parsed.mods;
function usedKeys() {
var used = {};
$.each(mods, function (_, mo) { used[mo.key] = true; });
return used;
}
/** Writes ta's value from the current word+mods and tells the
* caller's growth logic (bound to ta's "input" event) about it. */
function syncTa() {
ta.val(joinWordValue($.trim(wordInput.val()), mods));
ta.trigger("input");
}
/** Opens the same value-entry UI buildPills() uses for a NEW
* modifier, pre-filled, so an existing tag's value can be changed —
* used both for "add" (existingIdx === -1) and "edit" (click on an
* existing tag pill). */
function openValueEditor(mod, existingIdx) {
picker.hide();
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20)
.val(existingIdx === -1 ? "" : mods[existingIdx].value);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function apply() {
var v = $.trim(valInp.val());
if (v) {
if (existingIdx === -1) mods.push({ key: mod.key, value: v });
else mods[existingIdx].value = v;
syncTa();
renderMods();
}
valWrap.remove();
wordInput.focus();
}
confirm.on("click", apply);
cancel.on("click", function () { valWrap.remove(); wordInput.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); apply(); }
if (e.which === 27) { valWrap.remove(); wordInput.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
}
function modDef(key) {
var found = null;
$.each(WORD_MODIFIERS, function (_, wm) { if (wm.key === key) found = wm; });
return found;
}
function renderMods() {
modsRow.empty();
$.each(mods, function (idx, mo) {
var chip = $("<span class='wiktku-word-mod-chip' role='button' tabindex='0'></span>").text(modChipLabel(mo));
var x = $("<button type='button' class='wiktku-word-mod-chip-remove' title='Vê taybetmendiyê rake'>×</button>");
x.on("click", function (e) {
e.stopPropagation();
mods.splice(idx, 1);
syncTa();
renderMods();
});
// Click the chip itself (not its ×) to change the value —
// the tag (which modifier this is) can't be changed this
// way, only removed and re-added via ⊕, same as a word's
// text can only be edited, never its tags.
chip.on("click", function () {
var def = modDef(mo.key);
if (def) openValueEditor(def, idx);
});
chip.append(x);
modsRow.append(chip);
});
modsRow.toggle(!!mods.length);
}
renderMods();
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
openValueEditor(mod, -1);
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since mods (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// ⊕ (and the word's own remove ×) only make sense once there's an
// actual word — on the empty trailing "type here" box, a modifier
// would have nothing to attach to and there's nothing to remove.
function updateBtnVisibility() {
var hasWord = !!$.trim(wordInput.val());
btn.toggle(hasWord);
removeBtn.toggle(hasWord);
if (!hasWord) picker.hide();
}
updateBtnVisibility();
wordInput.on("input", function () {
updateBtnVisibility();
syncTa();
});
removeBtn.on("click", function () { li.remove(); });
// Lets restoreSubList's "fill an existing row" path set this word's
// value correctly (re-split into word+mods) instead of dumping the
// raw "word<key:value>" string straight into wordInput.
li.data("setRawValue", function (raw) {
var p = parseWordValue(raw);
wordInput.val(p.word);
mods = p.mods;
syncTa();
renderMods();
updateBtnVisibility();
});
// Close the picker once focus leaves the li entirely (clicking ⊕ or
// its value input blurs wordInput too, but focus stays inside li).
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
// Enter moves on instead of inserting a newline into what's meant
// to be a one-line field.
wordInput.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); wordInput[0].blur(); }
});
li.append(ta).append(modsRow).append(boxRow).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
keys.append(btn);
});
row.append(keys);
grid.append(row);
});
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
// useModifiers rows keep the word and its modifier tags in
// separate elements (see makeModifierLi) — setRawValue()
// re-splits val() the same way parsing it fresh would,
// instead of dumping the raw "word<key:value>" string into
// the visible word box.
var setRaw = items.eq(j).data("setRawValue");
if (useModifiers && typeof setRaw === "function") {
setRaw(val);
} else {
items.eq(j).find("textarea").val(val);
}
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
if (!useModifiers) return;
// A fresh empty row goes on the end so there's always somewhere
// to add the next word without first having to click into a
// filled one.
var filled = subList.find("li.wiktku-word-li");
if (filled.last().is(":last-child")) {
var nextTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-next-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow");
nextTa.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(makeModifierLi(nextTa));
}
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same one-box-per-word layout as the per-sense ⊕-modifier
// groups (see makeModifierLi) when this step has modifiers.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
6jgd7b6ted0mnn5psxu0gjdkvbdfkpg
6334300
6334292
2026-08-17T10:46:09Z
Wikihez
7344
6334300
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Short parenthetical prefix each modifier key gets in a pill's friendly
// display — e.g. "peyv<t:wate>" reads as "peyv (wate)", "peyv<c:mê>" as
// "peyv (cinsiyet: mê)". Keyed the same as WORD_MODIFIERS.
var WORD_MODIFIER_DISPLAY_PREFIX = {
t: "", // translation/gloss reads fine bare: "word (gloss)"
cuda: "cuda: ",
c: "cinsiyet: ",
e: "etîket: ",
ee: "etîket: ",
q: "wesf: ",
qq: "wesf: ",
crz: "cureyê rêzimanî: ",
hrf: "herfiyen: ",
ref: "çavkanî: "
};
/** Splits a raw "word<t:val1><c:val2>" field value into { word, mods }
* — mods is an array of {key, value} in the order they appear. Used to
* re-hydrate a word box's UI (plain word + separate modifier chips)
* from a value that was typed/restored in the combined raw form. */
function parseWordValue(raw) {
var m = /^([\s\S]*?)((?:<[a-zA-Z]+:[^<>]*>)+)$/.exec(raw || "");
if (!m) return { word: raw || "", mods: [] };
var mods = [];
var re = /<([a-zA-Z]+):([^<>]*)>/g, mm;
while ((mm = re.exec(m[2])) !== null) mods.push({ key: mm[1], value: mm[2] });
return { word: m[1], mods: mods };
}
/** Re-joins { word, mods } back into the raw "word<t:val1><c:val2>" form
* that readList()/output actually read. */
function joinWordValue(word, mods) {
if (!word) return "";
var out = word;
$.each(mods, function (_, mo) { out += "<" + mo.key + ":" + mo.value + ">"; });
return out;
}
/** Short "(key: value)" chip label for one attached modifier. */
function modChipLabel(mo) {
var prefix = WORD_MODIFIER_DISPLAY_PREFIX[mo.key];
return (prefix === undefined ? mo.key + ": " : prefix) + mo.value;
}
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` stays the hidden "source of truth" element — its value is always
* the raw "word<key:value>…" text that readList()/restoreSubList()/
* output already read and write, and the caller's own growth listener
* (onSubInput/refreshForTextField, bound to `ta`'s "input" event before
* this runs) keeps working untouched. Everything the person actually
* sees and types into is separate: a plain word box (wordInput, holds
* only the word — modifier tags can never be typed/corrupted through
* it, and it never changes shape, so it always looks and behaves like
* any other text field) plus a row of small tag pills, one per attached
* modifier, attached right above that word's box. Clicking a tag pill
* (not its ×) reopens it for editing its value; the ⊕ button adds more.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var narrow = (ta.attr("class") || "").split(/\s+/).indexOf("wiktku-start-narrow") !== -1;
// Deliberately NOT ta's own classes (wiktku-dfn-subfield/wiktku-
// start-step-field) — a lot of code elsewhere counts/indexes
// elements by those exact classes (restoreMultiField, readList's
// siblings, refresh()'s per-step field lookups, …); a second
// visible textarea matching them would throw those counts off.
// wiktku-word-input picks up the same look via styles.css instead.
var wordInput = $("<textarea></textarea>")
.attr("rows", ta.attr("rows") || 1)
.addClass("wiktku-word-input" + (narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", ta.attr("placeholder") || "");
ta.hide();
// Placeholders on the (hidden) canonical field are set by shared
// code like updateSubPlaceholders() that has no reason to know
// wordInput exists — mirror them across automatically instead of
// touching every one of those call sites.
if (window.MutationObserver) {
new MutationObserver(function () {
wordInput.attr("placeholder", ta.attr("placeholder") || "");
}).observe(ta[0], { attributes: true, attributeFilter: ["placeholder"] });
}
var modsRow = $("<div class='wiktku-word-mods'></div>").hide();
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var removeBtn = $("<button type='button' class='wiktku-word-remove' title='Vê peyvê rake'>×</button>").hide();
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var boxRow = $("<div class='wiktku-word-box-row'></div>")
.append(wordInput).append(btn).append(removeBtn);
// The word text and its modifiers are tracked separately — mods is
// never part of what wordInput holds, so typing into the word box
// can only ever change the word, never a tag. Parsed once from
// whatever ta already holds (e.g. a value restored from cache).
var parsed = parseWordValue(ta.val());
wordInput.val(parsed.word);
var mods = parsed.mods;
function usedKeys() {
var used = {};
$.each(mods, function (_, mo) { used[mo.key] = true; });
return used;
}
/** Writes ta's value from the current word+mods and tells the
* caller's growth logic (bound to ta's "input" event) about it. */
function syncTa() {
ta.val(joinWordValue($.trim(wordInput.val()), mods));
ta.trigger("input");
}
/** Opens the same value-entry UI buildPills() uses for a NEW
* modifier, pre-filled, so an existing tag's value can be changed —
* used both for "add" (existingIdx === -1) and "edit" (click on an
* existing tag pill). */
function openValueEditor(mod, existingIdx) {
picker.hide();
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20)
.val(existingIdx === -1 ? "" : mods[existingIdx].value);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function apply() {
var v = $.trim(valInp.val());
if (v) {
if (existingIdx === -1) mods.push({ key: mod.key, value: v });
else mods[existingIdx].value = v;
syncTa();
renderMods();
}
valWrap.remove();
wordInput.focus();
}
confirm.on("click", apply);
cancel.on("click", function () { valWrap.remove(); wordInput.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); apply(); }
if (e.which === 27) { valWrap.remove(); wordInput.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
}
function modDef(key) {
var found = null;
$.each(WORD_MODIFIERS, function (_, wm) { if (wm.key === key) found = wm; });
return found;
}
function renderMods() {
modsRow.empty();
$.each(mods, function (idx, mo) {
var chip = $("<span class='wiktku-word-mod-chip' role='button' tabindex='0'></span>").text(modChipLabel(mo));
var x = $("<button type='button' class='wiktku-word-mod-chip-remove' title='Vê taybetmendiyê rake'>×</button>");
x.on("click", function (e) {
e.stopPropagation();
mods.splice(idx, 1);
syncTa();
renderMods();
});
// Click the chip itself (not its ×) to change the value —
// the tag (which modifier this is) can't be changed this
// way, only removed and re-added via ⊕, same as a word's
// text can only be edited, never its tags.
chip.on("click", function () {
var def = modDef(mo.key);
if (def) openValueEditor(def, idx);
});
chip.append(x);
modsRow.append(chip);
});
modsRow.toggle(!!mods.length);
}
renderMods();
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
openValueEditor(mod, -1);
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since mods (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// ⊕ (and the word's own remove ×) only make sense once there's an
// actual word — on the empty trailing "type here" box, a modifier
// would have nothing to attach to and there's nothing to remove.
function updateBtnVisibility() {
var hasWord = !!$.trim(wordInput.val());
btn.toggle(hasWord);
removeBtn.toggle(hasWord);
if (!hasWord) picker.hide();
}
updateBtnVisibility();
wordInput.on("input", function () {
updateBtnVisibility();
syncTa();
});
removeBtn.on("click", function () { li.remove(); });
// Lets restoreSubList's "fill an existing row" path set this word's
// value correctly (re-split into word+mods) instead of dumping the
// raw "word<key:value>" string straight into wordInput.
li.data("setRawValue", function (raw) {
var p = parseWordValue(raw);
wordInput.val(p.word);
mods = p.mods;
syncTa();
renderMods();
updateBtnVisibility();
});
// Close the picker once focus leaves the li entirely (clicking ⊕ or
// its value input blurs wordInput too, but focus stays inside li).
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
// Enter moves on instead of inserting a newline into what's meant
// to be a one-line field.
wordInput.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); wordInput[0].blur(); }
});
li.append(ta).append(modsRow).append(boxRow).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
/** One character key — shared by both the plain grid (every
* group's row shown at once) and the grouped grid (one
* group's row shown at a time, picked from a dropdown). */
function makeKeyBtn(ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
return btn;
}
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) { keys.append(makeKeyBtn(ch)); });
row.append(keys);
grid.append(row);
});
return grid;
}
// The full IPA chart runs to ~180 symbols across 8 groups —
// too long to show all at once, so a dropdown picks one group
// (Bêdeng 1, Bêdeng 2, …) and only that group's keys show.
function buildGroupedGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid wiktku-ipa-grid--grouped'></div>");
var picker = $("<select class='wiktku-ipa-group-select wiktku-plain-select wiktku-start-narrow'></select>");
$.each(groups, function (i, g) {
picker.append($("<option></option>").val(i).text(g.label));
});
var keys = $("<span class='wiktku-ipa-keys'></span>");
function renderGroup(idx) {
keys.empty();
var g = groups[idx];
if (!g) return;
$.each(g.chars, function (_, ch) { keys.append(makeKeyBtn(ch)); });
}
picker.on("change", function () { renderGroup(this.selectedIndex); });
renderGroup(0);
grid.append($("<div class='wiktku-ipa-group-row'></div>").append(picker));
grid.append($("<div class='wiktku-ipa-row'></div>").append(keys));
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGroupedGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, aweyekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
// useModifiers rows keep the word and its modifier tags in
// separate elements (see makeModifierLi) — setRawValue()
// re-splits val() the same way parsing it fresh would,
// instead of dumping the raw "word<key:value>" string into
// the visible word box.
var setRaw = items.eq(j).data("setRawValue");
if (useModifiers && typeof setRaw === "function") {
setRaw(val);
} else {
items.eq(j).find("textarea").val(val);
}
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
if (!useModifiers) return;
// A fresh empty row goes on the end so there's always somewhere
// to add the next word without first having to click into a
// filled one.
var filled = subList.find("li.wiktku-word-li");
if (filled.last().is(":last-child")) {
var nextTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-next-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow");
nextTa.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(makeModifierLi(nextTa));
}
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same one-box-per-word layout as the per-sense ⊕-modifier
// groups (see makeModifierLi) when this step has modifiers.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
fsbgv3reyrl8m4h70vtpprjct1ns0fc
6334303
6334300
2026-08-17T11:32:41Z
Wikihez
7344
6334303
javascript
text/javascript
// Li ser [[vi:MediaWiki:Gadget-start.js]] hatiye refaktorkirin.
// implicit dependencies: StorageUtils (ObjectStorage), Gadget-citoid-helper.js (wikt.citoid)
/* global ObjectStorage, wikt */
//<nowiki>
$(function () {
if (window.location.href.includes("editintro=User:Wikihez/creation/intro")) {
return; // Do not run the script wextê WF:Bilez dişuxile
}
var ns = mw.config.get("wgNamespaceNumber");
if (ns && ns !== 108 &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/ceribandin$|\/test$/))) {
return;
}
var addLangMode = mw.util.getParamValue("wiktkuaddlang") === "1";
// ── Tercîh (user settings) panel stored via mw.storage (localStorage), globally
var WIKTKU_PREFS_KEY = "wiktku_start_prefs";
var WIKTKU_PREFS_DEFAULTS = {
// [] = show the full language list in the "ziman" step; otherwise
// only these codes are shown there (data.json codes, not names).
langCodes: [],
// true = never auto-open on a new/empty page — only via the
// "+Ziman/beşeke din" portlet link.
onlyOnClick: false,
// "auto" | "p-cactions" | "p-tb" | "p-views" — where the
// "+Ziman/beşeke din" portlet link is added.
portlet: "auto",
// true = suppress routine/informational mw.notify toasts (page
// loading, cache auto-restored, cache saved/cleared, "tê şandin…").
// Errors and direct results of an explicit action (like saving
// Tercîh itself) always show regardless of this setting.
quiet: false
};
function wiktkuLoadPrefs() {
if (!(window.mw && mw.storage)) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var raw;
try { raw = mw.storage.get(WIKTKU_PREFS_KEY); } catch (e) { raw = null; }
if (!raw) return $.extend({}, WIKTKU_PREFS_DEFAULTS);
var parsed;
try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; }
return $.extend({}, WIKTKU_PREFS_DEFAULTS, parsed);
}
function wiktkuSavePrefs(prefs) {
if (!(window.mw && mw.storage)) return false;
try {
mw.storage.set(WIKTKU_PREFS_KEY, JSON.stringify(prefs));
return true;
} catch (e) { return false; }
}
var wiktkuPrefs = wiktkuLoadPrefs();
// Routine/informational notices go through here so "quiet mode" can
// suppress them; errors and explicit-action confirmations (like the
// Tercîh save toast) call mw.notify directly instead, and always show.
function wiktkuNotify(message, options) {
if (wiktkuPrefs.quiet) return;
mw.notify(message, options);
}
// Removes every localStorage entry this script writes, across every
// page: the per-page draft caches (formCache below, one per page under
// "wiktku_start_draft_") and the per-textarea manual-resize heights
// ("wiktku_resize_height_…", set in util.js). Tercîh itself
// (WIKTKU_PREFS_KEY) is deliberately left alone — it's a setting the
// person is actively editing when they reach for this button inside the
// Tercîh dialog, not a draft/cache to discard.
//
// formDirty/autosaveDebounceTimer/formCache are declared further down
// (assigned by build()) but resolved by closure at call time, same as
// elsewhere in this file — this is only ever invoked after the wizard
// has built, since that's the only place the Tercîh button lives.
function wiktkuClearAllScriptCache() {
// Cancel the current page's pending autosave — otherwise it fires a
// few seconds later (or the periodic one does, within a minute)
// using the still-unblanked live form and silently re-writes the
// cache this just removed. Same reasoning as the single-page
// "Cache paqij bike" button.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
$("#wiktku-last-saved").text("");
// Keeps the current page's formCache instance (and its in-memory
// _cachedData) consistent with what's about to happen to its actual
// localStorage entries below.
try { formCache.Set({}); } catch (e) { /* storage unavailable — the raw sweep below still runs */ }
if (!window.localStorage) return;
var toRemove = [];
try {
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (!key) continue;
// The draft cache goes through [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage, which does NOT store it under the plain
// "wiktku_start_draft_<page>" name formCache is constructed
// with — it wraps it as "enwikt/ObjectStorage/
// wiktku_start_draft_<page>?v=<n>&Type=Data|Type". Matching
// as a substring (not a prefix) catches both the Data and
// Type entries, for every page and every cache version. The
// resize-height entries aren't wrapped — set directly via
// mw.storage in util.js — so those match as a plain prefix.
// Etîket/Dever/Kategorî suggestion overrides (WIKTKU_SUGGESTIONS_KEY_PREFIX,
// one key per field) are the same kind of direct mw.storage entry.
if (key.indexOf("wiktku_start_draft_") !== -1 || key.indexOf("wiktku_resize_height_") === 0 ||
key.indexOf(WIKTKU_SUGGESTIONS_KEY_PREFIX) === 0) {
toRemove.push(key);
}
}
} catch (e) { return; }
$.each(toRemove, function (_, key) {
try { localStorage.removeItem(key); } catch (e) { /* skip this key, keep clearing the rest */ }
});
}
// Lazily built the first time it's opened; reused after that (see
// wiktkuOpenPrefsDialog below, defined once we know OOUI is available).
var wiktkuPrefsWindowManager = null;
var wiktkuPrefsDialog = null;
function wiktkuOpenPrefsDialog() {
mw.loader.using(["oojs-ui-core", "oojs-ui-windows"], function () {
function WiktkuPrefsDialog(config) {
WiktkuPrefsDialog.super.call(this, config);
}
if (!wiktkuPrefsDialog) {
OO.inheritClass(WiktkuPrefsDialog, OO.ui.ProcessDialog);
WiktkuPrefsDialog.static.name = "wiktkuPrefsDialog";
WiktkuPrefsDialog.static.title = "Tercîhên min";
WiktkuPrefsDialog.static.actions = [
{ action: "save", label: "Qeyd bike", flags: ["primary", "progressive"] },
{ action: "cancel", label: "Betal bike", flags: "safe" },
{ action: "clearcache", label: "Hemû cache paqij bike", flags: "destructive" }
];
WiktkuPrefsDialog.prototype.initialize = function () {
WiktkuPrefsDialog.super.prototype.initialize.call(this);
this.langCodesInput = new OO.ui.TextInputWidget({
placeholder: "ku, zza, ckb (vala bihêle ji bo hemû zimanan)"
});
this.onlyOnClickCheckbox = new OO.ui.CheckboxInputWidget({});
this.quietCheckbox = new OO.ui.CheckboxInputWidget({});
this.portletDropdown = new OO.ui.DropdownInputWidget({
options: [
{ data: "auto", label: "Otomatîk (li gorî rûyê wîkiyê)" },
{ data: "p-cactions", label: "p-cactions - Li qismê Amûran" },
{ data: "left-navigation", label: "left-navigation - Li qismê Gotar-Gotûbêj" },
{ data: "p-tb", label: "p-tb - Li qismê Giştî" },
{ data: "p-views", label: "p-views - Li qismê Bixwîne" }
]
});
var fieldset = new OO.ui.FieldsetLayout({ label: "Eyar" });
fieldset.addItems([
new OO.ui.FieldLayout(this.langCodesInput, {
label: "Kodên zimanan ên ku tê xuyakirin li qismê \"ziman\"",
align: "top",
help: "Kodan bi virgulê (,) ji hev veqetîne. Vala bihêle da ku hemû ziman were nîşandan.",
helpInline: true
}),
new OO.ui.FieldLayout(this.onlyOnClickCheckbox, {
label: "Tenê veke wextê \"+Ziman/beşeke din\" were tikandin",
align: "inline",
help: "Eger were hilbijartin, ev amûr êdî otomatîk li ser rûpelên nû dest pê nake — divê meriv her carê pêl \"+Ziman/beşeke din\" bike.",
helpInline: true
}),
new OO.ui.FieldLayout(this.portletDropdown, {
label: "Cihê lînka \"+Ziman/beşeke din\"",
align: "top",
help: "\"Otomatîk\" li gorî rûyê wîkiyê hildibijêre.",
helpInline: true
}),
new OO.ui.FieldLayout(this.quietCheckbox, {
label: "Moda bêdeng (kêmtir agahdarî nîşan bide)",
align: "inline",
help: "Agahdariyên normal nîşan nede. Xeta her tim tên xuyakirin.",
helpInline: true
})
]);
this.$body.addClass("wiktku-prefs-dialog-body").append(fieldset.$element);
};
WiktkuPrefsDialog.prototype.getSetupProcess = function (data) {
return WiktkuPrefsDialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.langCodesInput.setValue(wiktkuPrefs.langCodes.join(", "));
this.onlyOnClickCheckbox.setSelected(!!wiktkuPrefs.onlyOnClick);
this.portletDropdown.setValue(wiktkuPrefs.portlet || "auto");
this.quietCheckbox.setSelected(!!wiktkuPrefs.quiet);
}, this);
};
WiktkuPrefsDialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === "save") {
return new OO.ui.Process(function () {
var codes = dialog.langCodesInput.getValue()
.split(/[,\s]+/)
.map(function (c) { return c.trim().toLowerCase(); })
.filter(function (c) { return !!c; });
wiktkuPrefs = {
langCodes: codes,
onlyOnClick: dialog.onlyOnClickCheckbox.isSelected(),
portlet: dialog.portletDropdown.getValue() || "auto",
quiet: dialog.quietCheckbox.isSelected()
};
wiktkuSavePrefs(wiktkuPrefs);
mw.notify(
"Tercîh hatin qeydkirin. Rûpelê ji nû ve bar bike (F5).",
{ type: "success", tag: "wiktku-prefs-save" }
);
dialog.close({ action: "save" });
});
}
if (action === "cancel") {
return new OO.ui.Process(function () {
dialog.close({ action: "cancel" });
});
}
if (action === "clearcache") {
return new OO.ui.Process(function () {
if (!window.confirm(
"Tu dixwazî hemû cache-ya vê amûrê ji her rûpelê paqij bikî " +
"(danûstandinên qeydkirî yên hemû rûpelan, bilindahiyên qutiyan)? " +
"Tercîhên li jor nayên jêbirin. Ev tişt nayê vegerandin."
)) return;
wiktkuClearAllScriptCache();
mw.notify("Hemû cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear-all" });
dialog.close({ action: "clearcache" });
});
}
return WiktkuPrefsDialog.super.prototype.getActionProcess.call(this, action);
};
wiktkuPrefsWindowManager = new OO.ui.WindowManager();
$(document.body).append(wiktkuPrefsWindowManager.$element);
wiktkuPrefsDialog = new WiktkuPrefsDialog();
wiktkuPrefsWindowManager.addWindows([wiktkuPrefsDialog]);
}
wiktkuPrefsWindowManager.openWindow(wiktkuPrefsDialog);
});
}
var targetContainer;
var skin = mw.config.get('skin');
if (wiktkuPrefs.portlet && wiktkuPrefs.portlet !== "auto") {
targetContainer = wiktkuPrefs.portlet;
} else if (skin === 'vector-2022') {
targetContainer = 'p-cactions';
} else if (skin === 'vector') {
targetContainer = 'p-tb';
} else {
targetContainer = 'p-views';
}
mw.util.addPortletLink(targetContainer,
mw.util.getUrl(mw.config.get("wgPageName"), { action: "submit", wiktkuaddlang: "1" }),
"+Ziman/beşeke din", "ca-wiktku-addlang",
"Li vê rûpelê, ziman an jî beşeke nû zêde bike");
// If "onlyOnClick" is set, the wizard should ONLY ever open via the
// "+Ziman/beşeke din" link — never automatically
var wiktkuNewPageDetected = $(".mw-newarticletext, .mw-newarticletextanon").length > 0;
if (wiktkuPrefs.onlyOnClick ? !addLangMode : (!addLangMode && !wiktkuNewPageDetected)) {
return;
}
// ── Shared helpers ───────────────────────────────────────────────────
// Generic text/UI utilities live in their own package file now — see the
// header of util.js for why. Aliased to bare names here so the ~400 call
// sites below read exactly as they did when these were local functions.
var wiktkuUtil = require("./start/util.js");
var capitalize = wiktkuUtil.capitalize;
var decapitalize = wiktkuUtil.decapitalize;
var escapeRegExp = wiktkuUtil.escapeRegExp;
var stripWikilinks = wiktkuUtil.stripWikilinks;
var splitTercimeIzah = wiktkuUtil.splitTercimeIzah;
var insertAtCursor = wiktkuUtil.insertAtCursor;
var foldDiacritics = wiktkuUtil.foldDiacritics;
var wrapNativeSelect = wiktkuUtil.wrapNativeSelect;
var initSelects = wiktkuUtil.initSelects;
var wiktkuInitManualResize = wiktkuUtil.initManualResize;
// Inserts a line-style template snippet ("* {{…}}") at the cursor,
// prefixing it with a newline unless it's going at the very start of the
// box or right after one already — otherwise picking a second template
// from the pron dropdown right after the first lands them both on the
// same line: "* {{ku-IPA}}* {{ku-kîte}}".
function insertLineSnippet(el, snippet) {
var before = el.value.slice(0, el.selectionStart);
if (before && !/\n$/.test(before)) snippet = "\n" + snippet;
insertAtCursor(el, snippet);
}
function langHeaderSource(codePattern) {
return "\n?==\\s*\\{\\{\\s*ziman\\s*\\|\\s*(" + codePattern + ")\\s*\\}\\}\\s*==";
}
// Delete button (real OOUI "trash" icon, destructive-flagged) shared by
// the Mane tab bar and the Beş tab bar. One button per bar — sitting
// right before "+ Maneyeke din" / "+ Beşeke din", single button acting on
// the one you're looking at, so it only ever has one thing it can delete.
// "oojs-ui.styles.icons-moderation" carries the trash glyph; it's not in
// the wizard's main startup dependency list since most people never see
// a delete button (needs 2+ Mane or Beş), so we only fetch it here, the
// first time one is actually built — the icon just appears once the CSS
// arrives, no need to wait for it before constructing the button.
function makeTrashButton(title) {
mw.loader.using("oojs-ui.styles.icons-moderation");
return new OO.ui.ButtonWidget({
icon: "trash",
flags: ["destructive"],
framed: false,
invisibleLabel: true,
label: title,
title: title,
classes: ["wiktku-trash-btn"]
});
}
function findLangHeader(text, code) {
var m = new RegExp(langHeaderSource(escapeRegExp(code))).exec(text);
return m ? { index: m.index, match: m[0] } : null;
}
var catSrcTemplates = null;
var catSrcTemplatesPromise = null;
function fetchCatSrcTemplates() {
if (catSrcTemplatesPromise) return catSrcTemplatesPromise;
catSrcTemplatesPromise = new mw.Api().get({
action: "query",
list: "categorymembers",
cmtitle: "Kategorî:Çavkanî",
cmnamespace: 10,
cmlimit: "max",
formatversion: 2
}).then(function (data) {
var members = (data.query && data.query.categorymembers) || [];
catSrcTemplates = $.map(members, function (m) {
// Pêşgira navî ya "Şablon:" rake.
return m.title.replace(/^[^:]+:/, "");
});
catSrcTemplates.sort(function (a, b) { return a.localeCompare(b, userLang); });
return catSrcTemplates;
}, function () {
catSrcTemplates = [];
return catSrcTemplates;
});
return catSrcTemplatesPromise;
}
var pageName = mw.config.get("wgPageName").replaceAll(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var pageNameRe = escapeRegExp(pageName);
var userLang = mw.config.get("wgUserLanguage");
var formCache = new ObjectStorage("wiktku_start_draft_" + mw.config.get("wgPageName"), 1);
var langsByCode = {};
// Looks up the display label for a language code, falling back to the
// code itself if it isn't known yet (e.g. langsByCode not loaded).
function getLangLabel(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
// Populated from [[MediaWiki:Gadget-start.js/data.json]] at runtime.
var metadata = {};
var ZARAVA_LANG_CODES = ["ckb", "sdh", "lki", "hac", "zza"];
// Converts a {code: label} object (langChoices/posChoices in data.json)
// into the {code, label[, sortKey]} array the rest of the script uses.
// isLang: true adds a sortKey and shows "(code)" after the label —
// language choices only.
function choicesFromObject(obj, isLang) {
var result = [];
$.each(obj || {}, function (code, label) {
var choice = { code: code, label: label };
if (isLang) choice.sortKey = label;
result.push(choice);
});
return result;
}
function getZaravaKeys(choices) {
var byCode = {};
$.each(choices || [], function (_, c) { byCode[c.code] = c; });
var result = [];
$.each(ZARAVA_LANG_CODES, function (_, code) {
if (byCode[code]) result.push(byCode[code]);
});
return result;
}
function needsTransliteration(langCode) {
var meta = metadata[langCode];
if (!meta || !meta.sc) return false;
var scripts = Array.isArray(meta.sc) ? meta.sc : [meta.sc];
return scripts.length > 0 && scripts[0] !== "Latn";
}
// metadata[lang].sc is either a single script string or an array of
// several — only the latter means the language is actually written in
// more than one script.
function langHasMultipleScripts(langCode) {
var meta = metadata[langCode];
return !!(meta && Array.isArray(meta.sc) && meta.sc.length > 1);
}
// Kodên cinsiyetê yên di metadata.g de — ji data.json tê dagirtin
var GENDER_BASE_LABELS = {};
// Number codes (y, s, d, p, p+, tp, j, j+, j/nj, nj, nj+, hj!, hj?) from
// data.json's "numberLabels". Same {code: {cat, display[, req]}} shape as
// the wiki's Lua number-type table; "display" can contain HTML (<abbr>)
// or wikilinks, so it's only used as-is in wikitext output, never dropped
// straight into an <option> (see stripMarkupForOption below).
var NUMBER_LABELS = {};
// Distinguishes the Jimar dropdown (j= param) from the Cîns dropdown
// (c= param): a code that's a key in NUMBER_LABELS is a number code,
// everything else is a gender code. Compound codes like "m-p"/"n-p" are
// gender+plural pairs and correctly stay on the gender side.
function isNumberCode(code) {
return Object.prototype.hasOwnProperty.call(NUMBER_LABELS, code);
}
// Strips the bit of markup "display" is allowed to contain so it's safe
// to use as plain <option> text (options can't render HTML or wikilinks):
// <abbr title="X">Y</abbr> -> "Y (X)", [[a|b]] -> "b", [[a]] -> "a".
function stripMarkupForOption(html) {
if (!html) return "";
return $.trim(
String(html)
.replace(/<abbr[^>]*title="([^"]*)"[^>]*>([\s\S]*?)<\/abbr>/gi, "$2 ($1)")
.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, "$1")
.replace(/\[\[([^\]]*)\]\]/g, "$1")
.replace(/<[^>]+>/g, "")
);
}
// Builds the Jimar dropdown's <option> text for one code: "j — jimarbar".
function numberOptionLabel(code) {
var def = NUMBER_LABELS[code];
if (!def) return code;
var text = stripMarkupForOption(def.display);
return text ? (code + " — " + text) : code;
}
// Sub-lists that only make sense for a plain (non-şablon) sense — hidden
// when a şablon (e.g. {{guharto|…}}) is chosen for that sense
var HIDDEN_ON_TPL_GROUPS = [
"wiktku-dfn-alternativ",
"wiktku-dfn-hevmane",
"wiktku-dfn-dijmane",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
];
function genderLabel(code) {
if (code === "p") return "Pirjimar";
var pluralMatch = code.match(/^(.+)-p$/);
if (pluralMatch && GENDER_BASE_LABELS[pluralMatch[1]]) {
return GENDER_BASE_LABELS[pluralMatch[1]] + " (pirjimar)";
}
return GENDER_BASE_LABELS[code] || code;
}
// ── Template explanations ────────────────────────────────────────────
// Every place a template can be picked shows the same row: a status
// dot, the name, a link to the template page, and an ⓘ. This is the ⓘ.
//
// Text comes from the template's own TemplateData where it has any, so
// the explanation stays right without anyone maintaining a second copy
// by hand — with the desc already in data.json as the fallback for
// templates that have none.
var wiktkuTplDataCache = {}; // name -> {desc, params[]} or null when absent
/** Fetches TemplateData for one template. Resolves to null if the
* template has none (a plain, undocumented template — very common). */
function fetchTemplateData(name) {
if (Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, name)) {
return $.Deferred().resolve(wiktkuTplDataCache[name]).promise();
}
return new mw.Api().get({
action: "templatedata",
titles: "Şablon:" + name,
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
var first = null;
$.each(pages, function (_, p) { if (!first) first = p; });
var out = null;
if (first) {
var desc = first.description;
// TemplateData descriptions are language maps when the
// template is documented in more than one language.
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(first.params || {}, function (key, p) {
var label = p && p.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(p && p.required) });
});
out = { desc: desc || "", params: params };
}
wiktkuTplDataCache[name] = out;
return out;
}, function () {
// No TemplateData, or the API is unhappy — cache the miss so a
// repeated ⓘ click doesn't re-request it every time.
wiktkuTplDataCache[name] = null;
return null;
});
}
/** Fetches TemplateData for many templates in one request and fills the
* cache; resolves either way, a template with no TemplateData caches as null. */
function prefetchTemplateData(names) {
var missing = $.grep(names || [], function (n) {
return n && !Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n);
});
if (!missing.length) return $.Deferred().resolve().promise();
// The API caps a titles list at 50 for normal users.
missing = missing.slice(0, 50);
return new mw.Api().get({
action: "templatedata",
titles: $.map(missing, function (n) { return "Şablon:" + n; }).join("|"),
redirects: 1,
formatversion: 2
}).then(function (data) {
var pages = (data && data.pages) || {};
$.each(pages, function (_, page) {
if (!page || !page.title) return;
var name = String(page.title).replace(/^[^:]+:/, "");
var desc = page.description;
if (desc && typeof desc === "object") desc = desc[userLang] || desc.en || desc[Object.keys(desc)[0]];
var params = [];
$.each(page.params || {}, function (key, prm) {
var label = prm && prm.label;
if (label && typeof label === "object") label = label[userLang] || label.en || label[Object.keys(label)[0]];
params.push({ name: key, label: label || "", required: !!(prm && prm.required) });
});
wiktkuTplDataCache[name] = { desc: desc || "", params: params };
});
// Anything the API didn't answer for simply has none — cache the
// miss so the per-row ⓘ doesn't re-request it one at a time.
$.each(missing, function (_, n) {
if (!Object.prototype.hasOwnProperty.call(wiktkuTplDataCache, n)) wiktkuTplDataCache[n] = null;
});
}, function () {
$.each(missing, function (_, n) { wiktkuTplDataCache[n] = null; });
});
}
/** Rewrites each tagged <option> in `sel` as "name — description", using
* TemplateData. Only fills rows that have no description yet, so the
* hand-written ones in data.json (which carry usage examples the API
* does not) are left exactly as they are. */
function applyTplDescriptions(sel) {
if (!sel || !sel.length) return;
var changed = false;
sel.find("option[data-tpl]").each(function () {
var opt = $(this);
var name = opt.attr("data-tpl");
if (opt.text().indexOf(" — ") !== -1) return; // already described
var td = wiktkuTplDataCache[name];
var desc = (td && td.desc) || localTplDesc(name);
if (!desc) return;
// One line: TemplateData descriptions can run to a paragraph.
desc = $.trim(String(desc).split("\n")[0]);
if (desc.length > 90) desc = desc.slice(0, 90) + "…";
opt.text(name + " — " + desc);
changed = true;
});
// The picker caches option text for searching, so it has to re-read.
if (changed) sel.trigger("wiktku:refresh");
}
/** Hand-written fallback description, from data.json.
*
* This is the escape hatch for templates with no TemplateData on the
* wiki: add a "desc" to the entry in data.json and it shows up in the
* dropdown and in the ⓘ immediately, no code change. TemplateData is
* still the better place — VisualEditor and every other tool reads it
* too — but this keeps the picker useful in the meantime.
*
* Works for any pool: pronTpls, etymTpls, tplDefs, jederDefs. */
function localTplDesc(name) {
var pools = [steps.pron.pronTpls, steps.etym.etymTpls, steps.dfn.tplDefs, steps.dfn.jederDefs];
var found = "";
$.each(pools, function (_, pool) {
if (pool && pool[name] && pool[name].desc) { found = pool[name].desc; return false; }
});
return found;
}
/** Opens the inline explanation panel for `name`, anchored under the
* picker it was opened from. */
function showTemplateInfo(name, anchorWrap) {
$(".wiktku-tpl-info").remove();
var panel = $("<div class='wiktku-tpl-info'></div>");
panel.append($("<div class='wiktku-tpl-info-name'></div>").text("{{" + name + "}}"));
var body = $("<div class='wiktku-tpl-info-desc'></div>").text("Tê barkirin…");
panel.append(body);
var params = $("<div class='wiktku-tpl-info-params'></div>");
panel.append(params);
panel.append($("<a class='wiktku-tpl-info-link' target='_blank' rel='noopener'></a>")
.attr("href", wiktkuUtil.tplPageUrl(name))
.text("Rûpela şablonê veke →"));
var close = $("<button type='button' class='wiktku-tpl-info-close' title='Bigire'>✕</button>");
close.on("click", function () { panel.remove(); });
panel.prepend(close);
$(anchorWrap).after(panel);
fetchTemplateData(name).then(function (td) {
var desc = (td && td.desc) || localTplDesc(name);
body.text(desc || "Ji bo vê şablonê îzahek nehatiye nivîsandin.");
if (td && td.params && td.params.length) {
params.append($("<div class='wiktku-tpl-info-params-title'></div>").text("Parametre:"));
var listEl = $("<ul></ul>");
$.each(td.params, function (_, p) {
var line = p.name + (p.label ? " — " + p.label : "") + (p.required ? " (mecbûrî)" : "");
listEl.append($("<li></li>").text(line));
});
params.append(listEl);
}
});
}
/** Marks a picker's <option>s as template rows so the searchable widget
* renders them with the dot / 🔗 / ⓘ affordances, and wires the ⓘ
* handler for that picker. `nameOf` maps an <option> to a template
* name — the pickers store different things in value= (a snippet for
* the insert dropdowns, the bare name for the sense selector). */
function markTemplateOptions(sel, nameOf) {
if (!sel || !sel.length) return;
var names = [];
sel.find("option").each(function () {
var opt = $(this);
var name = nameOf(opt);
if (!name) return;
opt.attr("data-tpl", name);
// Deprecated/typo templates are the "special cases only" ones.
opt.attr("data-tpl-status", /kevnbûyî|şaşnivîs|kêmpeyda/i.test(opt.text()) ? "yellow" : "green");
names.push(name);
});
sel.data("wiktkuTplInfo", showTemplateInfo);
// Fill in the descriptions. Matters most for the Mane picker, whose
// entries in data.json are names with no desc at all — "rastnivîs",
// "ku-Latn", "guharto" on their own. One request covers every
// template in the picker, cached for the per-row ⓘ as well.
prefetchTemplateData(names).always(function () {
applyTplDescriptions(sel);
});
}
/** Pulls the template name out of a snippet like "* {{ku-IPA}}" or
* "{{ji|SOURCELANG|CODE|…}}". */
function tplNameFromSnippet(snippet) {
var m = /\{\{\s*([^|}\n]+)/.exec(snippet || "");
return m ? $.trim(m[1]) : "";
}
// True if a sense has content beyond mane worth keeping (etîket, word
// lists, nimûne, jêder) — so it's still emitted with {{mane?}} if empty.
function senseHasExtraContent(s) {
if (s.etiket && $.grep(s.etiket, function (e) { return $.trim(e) !== ""; }).length) return true;
if (s.kategori && $.grep(s.kategori, function (k) { return $.trim(k) !== ""; }).length) return true;
if (s.alternativ.length || s.hevmane.length || s.dijmane.length ||
s.koordine.length || s.jornav.length || s.jernav.length || (s.dever && s.dever.length)) return true;
if (s.nimune && $.grep(s.nimune, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0) return true;
if (senseHasJeder(s)) return true;
return false;
}
// A technically-"complete" entry (Ziman + Cureyê rêzimanî filled) with
// nothing in Mane, Bi zaravayên din or Werger is still meaningfully
// empty, so this is checked separately at submit-click time with its
// own message.
function hasMinRequiredContent(values) {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasMane = dfnSenses.some(function (s) {
return (s.tpl && s.tplTitle) || (!s.tpl && (s.mane !== "" || senseHasExtraContent(s)));
});
if (hasMane) return true;
if (values.lang === "ku") {
var hasWerger = false;
$("#wiktku-start-trans .wiktku-trans-group").each(function () {
if (steps.trans.getGroupTranslations($(this)).length) hasWerger = true;
});
if (hasWerger) return true;
var hasZarava = false;
$("#wiktku-start-zarava .wiktku-zarava-group").each(function () {
if (steps.zarava.getGroupTranslations($(this)).length) hasZarava = true;
});
if (hasZarava) return true;
}
return false;
}
// Bolds the *first* occurrence of the page name
function boldPageNameOnce(text) {
if (!text || !pageNameRe) return text;
var re = new RegExp("(" + pageNameRe + "([^( |,|;|\\?|\\.|\\!)]+)?)", "i");
return text.replace(re, "'''$1'''");
}
// Converts literal line breaks typed into a nimûne/jêgirtin box into
// wikitext <br> tags,
function nlToBr(text) {
return text ? text.replace(/\r\n|\r|\n/g, "<br>") : text;
}
function escapeHtmlForBackdrop(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
}
// Mirrors a textarea's text, with a small highlighted marker placed
// right after each line that's followed by an actual typed/pasted
// linebreak. Rendered into a transparent backdrop layer behind the
// textarea (see ensureLinebreakBackdrop()), so only the point of the
// break is marked instead of the whole box.
function buildLinebreakBackdropHtml(text) {
var lines = text.split(/\r\n|\r|\n/);
return $.map(lines, function (line, i) {
var isLast = i === lines.length - 1;
return escapeHtmlForBackdrop(line) +
(isLast ? "" : "<mark class='wiktku-linebreak-mark'>↵</mark>\n");
}).join("");
}
// Copies the metrics that affect where text wraps/lands (font, line
// height, padding, border) from the real textarea onto its backdrop,
// via computed style rather than shared CSS classes — most of the
// box's own rules are written as "textarea.foo", which a <div> with
// the same class wouldn't match. Re-run on every update in case a
// responsive breakpoint changed the textarea's own font-size.
function syncLinebreakBackdropMetrics(textarea, backdrop) {
var cs = window.getComputedStyle(textarea[0]);
backdrop.css({
"box-sizing": cs.boxSizing,
"padding-top": cs.paddingTop,
"padding-right": cs.paddingRight,
"padding-bottom": cs.paddingBottom,
"padding-left": cs.paddingLeft,
"border-width": cs.borderWidth,
"border-style": "solid",
"font-family": cs.fontFamily,
"font-size": cs.fontSize,
"font-weight": cs.fontWeight,
"line-height": cs.lineHeight,
"letter-spacing": cs.letterSpacing
});
}
// Lazily wraps a textarea in its own positioning anchor (independent
// of .wiktku-resize-wrap, so this doesn't depend on load order versus
// wiktkuMakeManualResizable) and inserts the transparent backdrop
// layer used to mark the exact point of a linebreak. Safe to call
// more than once on the same textarea — the backdrop is cached.
function ensureLinebreakBackdrop(textarea) {
var existing = textarea.data("wiktkuLinebreakBackdrop");
if (existing) return existing;
textarea.wrap("<div class='wiktku-linebreak-wrap'></div>");
var backdrop = $("<div aria-hidden='true'></div>").addClass("wiktku-linebreak-backdrop");
textarea.before(backdrop);
textarea.data("wiktkuLinebreakBackdrop", backdrop);
textarea.on("scroll.wiktkuLinebreak", function () {
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
});
return backdrop;
}
// Flags a nimûne/jêgirtin textarea (via CSS class, for the border
// tint + tooltip) while it contains a typed/pasted linebreak, so the
// user notices before it's silently turned into <br> on output.
// Marks only the exact point of the break via a mirrored backdrop,
// rather than tinting the whole box.
function highlightLinebreak(el) {
var textarea = $(el);
var text = textarea.val();
var hasBreak = /\r|\n/.test(text);
textarea
.toggleClass("wiktku-has-linebreak", hasBreak)
.attr("title", hasBreak ? "Xeta nû dê wek <br> were nivîsandin" : "");
if (!hasBreak) {
var cached = textarea.data("wiktkuLinebreakBackdrop");
if (cached) cached.hide();
textarea.removeClass("wiktku-has-linebreak-source");
return;
}
var backdrop = ensureLinebreakBackdrop(textarea);
textarea.addClass("wiktku-has-linebreak-source");
syncLinebreakBackdropMetrics(textarea, backdrop);
backdrop.html(buildLinebreakBackdropHtml(text)).show();
backdrop.scrollTop(textarea.scrollTop()).scrollLeft(textarea.scrollLeft());
}
// Each nimûne item can be { text, tr, t }: text, transliteration, translation.
function formatNimune(nimunes, lang, hasJeder) {
var hasNimune = nimunes && $.grep(nimunes, function (n) {
return (typeof n === "object" ? n.text : n) !== "";
}).length > 0;
if (!hasNimune) {
return hasJeder ? "" : "\n#* {{nimûne?|" + lang + "}}";
}
return $.map(nimunes, function (n) {
var text = typeof n === "object" ? n.text : n;
if (!text) return undefined;
var bolded = nlToBr(boldPageNameOnce(text));
var tpl = "\n#: {{nm|" + lang + "|" + bolded;
if (typeof n === "object") {
if (n.tr) tpl += "|tr=" + nlToBr(n.tr);
// Translation as third positional param (no key needed).
if (n.t) tpl += "|" + nlToBr(n.t);
}
// Short examples fit on one line together with their translation.
// rêz=1 is not used for ku — definitions are shown on separate lines there.
if (text.length < 60 && lang !== "ku") tpl += "|rêz=1";
tpl += "}}";
return tpl;
}).join("");
}
// Formats jêder (citation) entries into "#* {{jêder…|…}}" lines; skips
// entries with no quote text. Falls back to the generic {{jêder}}
// template when no specific template was chosen.
function formatJeder(jederList, lang) {
if (!jederList || !jederList.length) return "";
return $.map(jederList, function (jeder) {
if (!jeder || !jeder.quote) return undefined;
var quote = nlToBr(boldPageNameOnce(jeder.quote));
var quoteTr = nlToBr(jeder.quoteTr);
var quoteT = nlToBr(jeder.quoteT);
if (jeder.jederTpl) {
var cite = "\n#* {{" + jeder.jederTpl + "|" + lang;
cite += "|jêgirtin=" + quote;
if (quoteTr) cite += "|tr=" + quoteTr;
if (quoteT) cite += "|tercime=" + quoteT;
$.each(jeder.jêderFields || {}, function (key, val) {
if (val) cite += "|" + key + "=" + val;
});
cite += "}}";
return cite;
}
var out = "\n#* {{jêder|" + lang + "|jêgirtin=" + quote;
if (quoteTr) out += "|tr=" + quoteTr;
if (quoteT) out += "|tercime=" + quoteT;
// A şablon picked from Kategorî:Çavkanî is transcluded as the
// source param (e.g. |{{Kovara X}}); manual text is used as-is.
var srcParam = jeder.jederCatTpl ? ("{{" + jeder.jederCatTpl + "}}") :
(jeder.jederManual ? jeder.jederManual : "çavkanî?");
// A positional param containing "=" gets parsed as its own key=value
// pair by MediaWiki and breaks the template, so force it to be
// explicitly named (|2=) whenever that's the case.
out += (srcParam.indexOf("=") !== -1 ? "|2=" : "|") + srcParam;
out += "}}";
return out;
}).join("");
}
// True if a sense has at least one jêder entry with quote text.
function senseHasJeder(sense) {
return !!(sense.jeder && $.grep(sense.jeder, function (j) {
return j && j.quote;
}).length > 0);
}
// Builds "{{tplName|lang|part1|part2|…}}" from a sense field (kategori,
// etiket), wrapped inline (" {{…}} ") or on its own line ("\n#: {{…}}").
function buildSenseTpl(sense, field, lang, tplName) {
if (!sense[field] || !sense[field].length) return "";
var parts = sense[field].map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
var call = "{{" + tplName + "|" + lang + "|" + parts.join("|") + "}}";
return call
}
// Dever/Şîve values only ever get their own {{dever|…}} call for ku —
// no |lang| param, since the template is Kurdish-dialect-specific. For
// every other language there's no dever template at all; see
// buildEtiketTpl() below, which folds them into {{e|}} instead.
function buildDeverTpl(sense, lang) {
if (lang !== "ku" || !sense.dever || !sense.dever.length) return "";
var parts = sense.dever.map(function (p) { return $.trim(p); })
.filter(function (p) { return p !== ""; });
if (!parts.length) return "";
return "{{dever|" + parts.join("|") + "}}";
}
// Builds the {{e|lang|…}} etîket call. For non-ku senses, Dever/Şîve
// values are appended after the regular etîket values instead of going
// into their own {{dever|}} call (see buildDeverTpl() above).
function buildEtiketTpl(sense, lang) {
var etiket = (sense.etiket || []).slice();
if (lang !== "ku" && sense.dever && sense.dever.length) {
etiket = etiket.concat(sense.dever);
}
return buildSenseTpl({ etiket: etiket }, "etiket", lang, "e");
}
/**
* Assembles the nimûne + jêder wikitext block for a sense. Shared by
* both the template-sense and normal-mane-sense branches below.
*/
function buildCite(sense, lang) {
return formatNimune(sense.nimune, lang, senseHasJeder(sense)) +
formatJeder(sense.jeder, lang);
}
// ── Inline modifier system ───────────────────────────────────────────────
// Quick-pick suggestions for the Dever/Şîve chip picker on each sense
// (see buildSenseBlock's deverWrap). Free text is always accepted too —
// this list only saves typing for the common regional names.
var DEVER_SUGGESTIONS = [
"Behdînan", "Badînî", "Serhed", "Botan", "Hekarî",
"Mêrdîn", "Colemêrg", "Efrîn", "Dihok", "Qamişlo", "Sûriye"
];
// Appends a ⊕ button to a word input <li>. Clicking it shows a compact
// modifier picker; selecting one prompts for the value and appends
// <modifier:value> to the textarea's current content.
var WORD_MODIFIERS = [
{ key: "t", label: "t: tercime / îzah", hint: "Tercimeya kelîmeyê an îzaha wê" },
{ key: "cuda", label: "cuda: alt display", hint: "Forma nîşankirinê ya alternatîf" },
{ key: "c", label: "c: cinsiyet", hint: "Cinsiyeta kelîmeyê" },
{ key: "e", label: "e: etîket (li pêşiyê)", hint: "Etîketek li pêşiya kelîmeyê" },
{ key: "ee", label: "ee: etîket (li dawiyê)", hint: "Etîketek li dawiya kelîmeyê" },
{ key: "q", label: "q: wesf (li pêşiyê)", hint: "Wesf li pêşiya kelîmeyê" },
{ key: "qq", label: "qq: wesf (li dawiyê)", hint: "Wesf li dawiya kelîmeyê" },
{ key: "crz", label: "crz: cureyê rêzimanî", hint: "Cureyê rêzimanî yê kelîmeyê" },
{ key: "hrf", label: "hrf: herfiyen", hint: "Herfiyen, manaya rasterast" },
{ key: "ref", label: "ref: çavkanî", hint: "Çavkanî" }
];
// Short parenthetical prefix each modifier key gets in a pill's friendly
// display — e.g. "peyv<t:wate>" reads as "peyv (wate)", "peyv<c:mê>" as
// "peyv (cinsiyet: mê)". Keyed the same as WORD_MODIFIERS.
var WORD_MODIFIER_DISPLAY_PREFIX = {
t: "", // translation/gloss reads fine bare: "word (gloss)"
cuda: "cuda: ",
c: "cinsiyet: ",
e: "etîket: ",
ee: "etîket: ",
q: "wesf: ",
qq: "wesf: ",
crz: "cureyê rêzimanî: ",
hrf: "herfiyen: ",
ref: "çavkanî: "
};
/** Splits a raw "word<t:val1><c:val2>" field value into { word, mods }
* — mods is an array of {key, value} in the order they appear. Used to
* re-hydrate a word box's UI (plain word + separate modifier chips)
* from a value that was typed/restored in the combined raw form. */
function parseWordValue(raw) {
var m = /^([\s\S]*?)((?:<[a-zA-Z]+:[^<>]*>)+)$/.exec(raw || "");
if (!m) return { word: raw || "", mods: [] };
var mods = [];
var re = /<([a-zA-Z]+):([^<>]*)>/g, mm;
while ((mm = re.exec(m[2])) !== null) mods.push({ key: mm[1], value: mm[2] });
return { word: m[1], mods: mods };
}
/** Re-joins { word, mods } back into the raw "word<t:val1><c:val2>" form
* that readList()/output actually read. */
function joinWordValue(word, mods) {
if (!word) return "";
var out = word;
$.each(mods, function (_, mo) { out += "<" + mo.key + ":" + mo.value + ">"; });
return out;
}
/** Short "(key: value)" chip label for one attached modifier. */
function modChipLabel(mo) {
var prefix = WORD_MODIFIER_DISPLAY_PREFIX[mo.key];
return (prefix === undefined ? mo.key + ": " : prefix) + mo.value;
}
// Sub-groups (inside a sense/definition block) that get the same ⊕
// modifier button as makeWordListStep fields.
var MODIFIER_SUBGROUPS = {
"wiktku-dfn-alternativ": true,
"wiktku-dfn-hevmane": true,
"wiktku-dfn-dijmane": true,
"wiktku-dfn-koordine": true,
"wiktku-dfn-jornav": true,
"wiktku-dfn-jernav": true
};
/**
* Wraps a freshly-created textarea in a <li> with a ⊕ modifier button.
* `ta` stays the hidden "source of truth" element — its value is always
* the raw "word<key:value>…" text that readList()/restoreSubList()/
* output already read and write, and the caller's own growth listener
* (onSubInput/refreshForTextField, bound to `ta`'s "input" event before
* this runs) keeps working untouched. Everything the person actually
* sees and types into is separate: a plain word box (wordInput, holds
* only the word — modifier tags can never be typed/corrupted through
* it, and it never changes shape, so it always looks and behaves like
* any other text field) plus a row of small tag pills, one per attached
* modifier, attached right above that word's box. Clicking a tag pill
* (not its ×) reopens it for editing its value; the ⊕ button adds more.
* Returns the <li>.
*/
function makeModifierLi(ta) {
var li = $("<li class='wiktku-word-li'></li>");
var narrow = (ta.attr("class") || "").split(/\s+/).indexOf("wiktku-start-narrow") !== -1;
// Deliberately NOT ta's own classes (wiktku-dfn-subfield/wiktku-
// start-step-field) — a lot of code elsewhere counts/indexes
// elements by those exact classes (restoreMultiField, readList's
// siblings, refresh()'s per-step field lookups, …); a second
// visible textarea matching them would throw those counts off.
// wiktku-word-input picks up the same look via styles.css instead.
var wordInput = $("<textarea></textarea>")
.attr("rows", ta.attr("rows") || 1)
.addClass("wiktku-word-input" + (narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", ta.attr("placeholder") || "");
ta.hide();
// Placeholders on the (hidden) canonical field are set by shared
// code like updateSubPlaceholders() that has no reason to know
// wordInput exists — mirror them across automatically instead of
// touching every one of those call sites.
if (window.MutationObserver) {
new MutationObserver(function () {
wordInput.attr("placeholder", ta.attr("placeholder") || "");
}).observe(ta[0], { attributes: true, attributeFilter: ["placeholder"] });
}
var modsRow = $("<div class='wiktku-word-mods'></div>").hide();
var btn = $("<button type='button' class='wiktku-mod-btn' title='Modifiyerê lê zêde bike'>⊕</button>");
var removeBtn = $("<button type='button' class='wiktku-word-remove' title='Vê peyvê rake'>×</button>").hide();
var picker = $("<div class='wiktku-mod-picker'></div>").hide();
var boxRow = $("<div class='wiktku-word-box-row'></div>")
.append(wordInput).append(btn).append(removeBtn);
// The word text and its modifiers are tracked separately — mods is
// never part of what wordInput holds, so typing into the word box
// can only ever change the word, never a tag. Parsed once from
// whatever ta already holds (e.g. a value restored from cache).
var parsed = parseWordValue(ta.val());
wordInput.val(parsed.word);
var mods = parsed.mods;
function usedKeys() {
var used = {};
$.each(mods, function (_, mo) { used[mo.key] = true; });
return used;
}
/** Writes ta's value from the current word+mods and tells the
* caller's growth logic (bound to ta's "input" event) about it. */
function syncTa() {
ta.val(joinWordValue($.trim(wordInput.val()), mods));
ta.trigger("input");
}
/** Opens the same value-entry UI buildPills() uses for a NEW
* modifier, pre-filled, so an existing tag's value can be changed —
* used both for "add" (existingIdx === -1) and "edit" (click on an
* existing tag pill). */
function openValueEditor(mod, existingIdx) {
picker.hide();
var valWrap = $("<span class='wiktku-mod-val-wrap'></span>");
var valInp = $("<input type='text' class='wiktku-mod-val-input'>")
.attr("placeholder", mod.hint + "…")
.attr("size", 20)
.val(existingIdx === -1 ? "" : mods[existingIdx].value);
var confirm = $("<button type='button' class='wiktku-mod-confirm'>✓</button>");
var cancel = $("<button type='button' class='wiktku-mod-cancel'>✕</button>");
function apply() {
var v = $.trim(valInp.val());
if (v) {
if (existingIdx === -1) mods.push({ key: mod.key, value: v });
else mods[existingIdx].value = v;
syncTa();
renderMods();
}
valWrap.remove();
wordInput.focus();
}
confirm.on("click", apply);
cancel.on("click", function () { valWrap.remove(); wordInput.focus(); });
valInp.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); apply(); }
if (e.which === 27) { valWrap.remove(); wordInput.focus(); }
});
valWrap.append(valInp).append(confirm).append(cancel);
li.append(valWrap);
valInp.focus();
}
function modDef(key) {
var found = null;
$.each(WORD_MODIFIERS, function (_, wm) { if (wm.key === key) found = wm; });
return found;
}
function renderMods() {
modsRow.empty();
$.each(mods, function (idx, mo) {
var chip = $("<span class='wiktku-word-mod-chip' role='button' tabindex='0'></span>").text(modChipLabel(mo));
var x = $("<button type='button' class='wiktku-word-mod-chip-remove' title='Vê taybetmendiyê rake'>×</button>");
x.on("click", function (e) {
e.stopPropagation();
mods.splice(idx, 1);
syncTa();
renderMods();
});
// Click the chip itself (not its ×) to change the value —
// the tag (which modifier this is) can't be changed this
// way, only removed and re-added via ⊕, same as a word's
// text can only be edited, never its tags.
chip.on("click", function () {
var def = modDef(mo.key);
if (def) openValueEditor(def, idx);
});
chip.append(x);
modsRow.append(chip);
});
modsRow.toggle(!!mods.length);
}
renderMods();
// The 10 pills (each its own click handler) are built lazily, on
// first open, rather than for every word field up front — this runs
// once per makeWordListStep/sub-list field, per sense, and most
// fields never touch their ⊕ picker at all. Building it eagerly was
// a big share of why switching Beş tabs was slow: restoreDfn()
// rebuilds every sense's fields from scratch on every switch.
var pillsBuilt = false;
function buildPills() {
if (pillsBuilt) return;
pillsBuilt = true;
$.each(WORD_MODIFIERS, function (_, mod) {
var pill = $("<button type='button' class='wiktku-mod-pill'></button>").text(mod.label);
pill.attr("title", mod.hint);
pill.data("mod-key", mod.key);
pill.on("click", function (e) {
e.preventDefault();
if (pill.prop("disabled")) return; // already used on this word
openValueEditor(mod, -1);
});
picker.append(pill);
});
}
btn.on("click", function (e) {
e.preventDefault();
// Close any other open pickers in the same list.
li.closest("ol, ul").find(".wiktku-mod-picker").not(picker).hide();
buildPills();
// Grey out modifiers already used on this word — refreshed on
// every open since mods (and thus what's "used") can change.
var used = usedKeys();
picker.find(".wiktku-mod-pill").each(function () {
var isUsed = !!used[$(this).data("mod-key")];
$(this).prop("disabled", isUsed).toggleClass("wiktku-mod-pill-used", isUsed);
});
picker.toggle();
});
// ⊕ (and the word's own remove ×) only make sense once there's an
// actual word — on the empty trailing "type here" box, a modifier
// would have nothing to attach to and there's nothing to remove.
function updateBtnVisibility() {
var hasWord = !!$.trim(wordInput.val());
btn.toggle(hasWord);
removeBtn.toggle(hasWord);
if (!hasWord) picker.hide();
}
updateBtnVisibility();
wordInput.on("input", function () {
updateBtnVisibility();
syncTa();
});
removeBtn.on("click", function () { li.remove(); });
// Lets restoreSubList's "fill an existing row" path set this word's
// value correctly (re-split into word+mods) instead of dumping the
// raw "word<key:value>" string straight into wordInput.
li.data("setRawValue", function (raw) {
var p = parseWordValue(raw);
wordInput.val(p.word);
mods = p.mods;
syncTa();
renderMods();
updateBtnVisibility();
});
// Close the picker once focus leaves the li entirely (clicking ⊕ or
// its value input blurs wordInput too, but focus stays inside li).
li.on("focusout", function () {
setTimeout(function () {
if (!$.contains(li[0], document.activeElement)) picker.hide();
}, 150);
});
// Enter moves on instead of inserting a newline into what's meant
// to be a one-line field.
wordInput.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); wordInput[0].blur(); }
});
li.append(ta).append(modsRow).append(boxRow).append(picker);
return li;
}
/**
* Builds a "chip + toggle" field (design "1c", first built for
* Dever/Şîve): existing values render as small removable chips; a
* "+ <label>" toggle opens a compact panel with optional quick-pick
* suggestion pills plus free text entry. Keeps the form from growing
* for the (usually most) senses that leave the field empty — no
* separate "advanced" section needed to hide it behind.
*
* opts: {
* wrapCls: unique class on the wrapper, e.g. "wiktku-dfn-dever" —
* getSenses()/restoreDfn() scope chip reads/writes by it,
* and it keys the persisted suggestion overrides below.
* toggleLabel: e.g. "+ Dever/Şîve"
* hint: panel's question/help line (optional)
* suggestions: array of quick-pick strings (optional)
* placeholder: free-text input placeholder (optional)
* onOpen: called right before this field's panel opens, so a
* caller can close sibling fields' panels (optional)
* }
* The panel is returned separately from wrap (not appended inside it) —
* callers place all fields' wraps together in a toggle row and all
* fields' panels together in a panel area below, so an open panel never
* shifts the position of other fields' toggles.
* Suggestion pills get a × too: removing a built-in one hides it from
* then on, and anything typed into the free-text box joins the pick
* list for next time — both persisted per wrapCls via mw.storage, not
* tied to any one page or entry.
* Returns { wrap, panel, addChip, values, setOpenDefault, close }.
*/
var WIKTKU_SUGGESTIONS_KEY_PREFIX = "wiktku_suggestions_";
function loadSuggestionOverrides(wrapCls) {
if (!(window.mw && mw.storage)) return { removed: [], added: [] };
var raw;
try { raw = mw.storage.get(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls); } catch (e) { raw = null; }
var parsed = {};
if (raw) { try { parsed = JSON.parse(raw); } catch (e) { parsed = {}; } }
return { removed: parsed.removed || [], added: parsed.added || [] };
}
function saveSuggestionOverrides(wrapCls, overrides) {
if (!(window.mw && mw.storage)) return;
try { mw.storage.set(WIKTKU_SUGGESTIONS_KEY_PREFIX + wrapCls, JSON.stringify(overrides)); } catch (e) { /* storage unavailable — override just won't persist */ }
}
function buildChipField(opts) {
var wrap = $("<div></div>").addClass("wiktku-dfn-chipfield-wrap " + opts.wrapCls + "-wrap");
var chipsRow = $("<span class='wiktku-dfn-chipfield-chips'></span>");
var toggle = $("<button type='button' class='wiktku-dfn-chipfield-toggle'></button>").text(opts.toggleLabel);
var panel = $("<div class='wiktku-dfn-chipfield-panel'></div>").hide();
var suggestRow = $("<div class='wiktku-dfn-chipfield-suggest'></div>");
var input = $("<input type='text' class='wiktku-dfn-chipfield-input'>")
.attr("placeholder", opts.placeholder || ((opts.suggestions && opts.suggestions.length) ? "An ya nû binivîse…" : "Binivîse…"));
var addBtn = $("<button type='button' class='wiktku-dfn-chipfield-add'>Qeyd bike</button>");
// Once the person opens/closes the panel themselves, lang-driven
// syncing (see setOpenDefault) backs off rather than fighting them.
var userToggled = false;
// The pick list shown is opts.suggestions with the person's own
// edits layered on top — see loadSuggestionOverrides above.
var overrides = loadSuggestionOverrides(opts.wrapCls);
var removedSuggestions = overrides.removed;
var addedSuggestions = overrides.added;
function persistOverrides() {
saveSuggestionOverrides(opts.wrapCls, { removed: removedSuggestions, added: addedSuggestions });
}
function allSuggestions() {
return $.grep(opts.suggestions || [], function (s) { return removedSuggestions.indexOf(s) === -1; })
.concat(addedSuggestions);
}
function learnSuggestion(val) {
if (!val || allSuggestions().indexOf(val) !== -1) return;
addedSuggestions.push(val);
persistOverrides();
}
function forgetSuggestion(name) {
var idx = addedSuggestions.indexOf(name);
if (idx !== -1) addedSuggestions.splice(idx, 1);
else if (removedSuggestions.indexOf(name) === -1) removedSuggestions.push(name);
persistOverrides();
renderSuggest();
// renderSuggest() just destroyed the pill's × button the click
// came from — which was focused — dropping focus outside the
// panel entirely. Left alone, that reads as "focus left the
// widget" to the focusout handler below and closes the whole
// panel. Move focus back inside before that check runs.
input.focus();
}
function values() {
var vals = [];
chipsRow.find(".wiktku-dfn-chipfield-chip").each(function () {
vals.push($(this).attr("data-value"));
});
return vals;
}
function renderSuggest() {
var taken = values();
suggestRow.empty();
$.each(allSuggestions(), function (_, name) {
if (taken.indexOf(name) !== -1) return;
var pill = $("<span role='button' class='wiktku-dfn-chipfield-pill'></span>").text(name);
pill.on("click", function () { addChip(name); input.val("").focus(); });
var pillRemove = $("<button type='button' class='wiktku-dfn-chipfield-pill-remove' title='Vê pêşniyarê veşêre'>×</button>");
pillRemove.on("click", function (e) {
e.stopPropagation(); // don't also trigger the pill's own add-chip click
forgetSuggestion(name);
});
pill.append(pillRemove);
suggestRow.append(pill);
});
suggestRow.toggle(!!suggestRow.children().length);
}
function addChip(val) {
val = $.trim(val || "");
if (!val || values().indexOf(val) !== -1) return;
var chip = $("<span class='wiktku-dfn-chipfield-chip'></span>").attr("data-value", val).text(val);
var remove = $("<button type='button' class='wiktku-dfn-chipfield-remove' title='Rake'>×</button>");
// Same focus-loss hazard as forgetSuggestion() below — refocus
// the input so removing a chip while the panel is open doesn't
// also close the panel.
remove.on("click", function () { chip.remove(); renderSuggest(); input.focus(); });
chip.append(remove);
chip.insertBefore(toggle); // chips in insertion order, toggle stays last
renderSuggest();
}
toggle.on("click", function () {
userToggled = true;
if (panel.is(":visible")) { panel.hide(); return; }
if (opts.onOpen) opts.onOpen();
renderSuggest();
panel.show();
input.focus();
});
// Anything typed and committed by hand joins the pick list too, not
// just the current sense's chips.
function commitInput() {
var val = $.trim(input.val() || "");
if (val) {
addChip(val);
learnSuggestion(val);
}
input.val("").focus();
}
addBtn.on("click", commitInput);
input.on("keydown", function (e) {
if (e.which === 13) { e.preventDefault(); commitInput(); }
});
// Close the panel once focus leaves toggle+panel together — they're
// siblings in the DOM (not nested), so both must be checked.
function focusStillInside() {
return $.contains(wrap[0], document.activeElement) || $.contains(panel[0], document.activeElement);
}
wrap.add(panel).on("focusout", function () {
setTimeout(function () {
if (!focusStillInside()) panel.hide();
}, 150);
});
if (opts.hint) panel.append($("<div class='wiktku-dfn-chipfield-hint'></div>").text(opts.hint));
panel.append(suggestRow).append(
$("<div class='wiktku-dfn-chipfield-input-row'></div>").append(input).append(addBtn)
);
chipsRow.append(toggle);
wrap.append(chipsRow);
return {
wrap: wrap,
panel: panel,
addChip: addChip,
values: values,
/** Sets the panel's default open/closed state — a no-op once
* the person has manually toggled it themselves this session. */
setOpenDefault: function (open) {
if (userToggled) return;
// Same as the toggle click handler: (re)build the suggestion
// pills whenever the panel opens, not just on a manual click —
// otherwise a panel opened by this default-state sync (e.g.
// Dever/Şîve starting open for ku) shows an empty pill row
// until the person closes and reopens it themselves.
if (open) {
if (opts.onOpen) opts.onOpen();
renderSuggest();
}
panel.toggle(!!open);
},
/** Force-closes the panel as though the person had done it
* themselves, so a later setOpenDefault() doesn't reopen it out
* from under them — used by onOpen to keep only one of these
* fields' panels open at a time (see the call site). */
close: function () {
userToggled = true;
panel.hide();
}
};
}
function makeWordListStep(id, name, question_fmt) {
return {
id: id,
name: name,
question_fmt: question_fmt,
multiple: true,
narrow: true,
modifiers: true,
// Function instead of a plain string so it can name the current
// language — computed fresh at build time and re-applied by
// refresh() whenever the language changes.
placeholder: function () {
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return "Kelîme" + (langLabel ? " bi " + langLabel : "") + "…";
},
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
var words = $.grep(values[id] || [], function (w) { return !!w; });
var header = capitalize(name);
if (!words.length) return "";
var lang = values.lang;
var section = "\n==== " + header + " ====\n";
var params = "cure=" + header + "\n" +
$.map(words, function (w) {
return "|" + w;
}).join("\n");
return section +
"{{kol3|" + lang + "|" + params + "\n}}\n";
} };
}
var steps = {
lang: {
id: "lang",
required: true,
name: "ziman",
question_fmt: '"' + pageName + '" bi çi zimanî ye?',
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (langChoices).
choices: [],
output: function (values) {
return "== {{ziman|" + values.lang + "}} ==\n";
}
},
pron: {
id: "pron",
name: "bilêvkirin",
question_fmt: '"' + pageName + '" çawa tê telafûzkirin?',
// pronTpls populated from data.json
pronTpls: {},
visible: function (values) {
if (!values.lang) return false;
// addLangMode: skip pron only if this lang already has a section.
if (addLangMode) {
// Clearing the page makes existing content irrelevant.
var clearPageCheck = $("#wiktku-clear-page-check");
if (clearPageCheck.length && clearPageCheck.prop("checked")) {
return true;
}
var existing = $("#wpTextbox1").val() || "";
return !findLangHeader(existing, values.lang);
}
return true;
},
// Fills the insert-dropdown from pronTpls, filtered by language.
refreshTplSelect: function () {
var sel = $("#wiktku-start-pron .wiktku-tpl-insert-select");
if (!sel.length) return;
var lang = getValues().lang || "";
sel.find("option[value!='']").remove();
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-pron-wrap").length) return null;
var wrap = $("<div class='wiktku-pron-wrap'></div>");
// Reassures the user this field is optional — the dropdown
// below writes the template for them, no syntax needed up front.
wrap.append(
$("<p class='wiktku-step-hint'></p>").text(
"Ev qad ne mecbûrî ye. Heke tu nizanî kîjan şablonî hilbijêrî, vale bihêle."
)
);
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-pron-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-pron-text")
.attr("rows", 4)
.attr("placeholder", "Nimûne: {{IPA|ku|/pɛɪ̯n/}} yan {{kîte|ku|he|ce|ki|rin}}");
// Template insert dropdown — filtered by language.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike ");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var lang = getValues().lang || "";
$.each(steps.pron.pronTpls, function (name, tpl) {
// Filter: if tpl.langs exists, only show if lang is in that list; otherwise show always
var allowed = !tpl.langs || tpl.langs.indexOf(lang) !== -1;
if (allowed) {
sel.append($("<option></option>")
.val(tpl.snippet)
.text(name + " — " + tpl.desc)
.attr("data-nolang", tpl.noLang ? "1" : ""));
}
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
// Check if the selected option has noLang flag
var selectedOption = sel.find("option:selected");
var hasNoLang = selectedOption.attr("data-nolang") === "1";
if (!hasNoLang) {
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertLineSnippet(ta[0], snippet);
$(this).val("");
// Clear the picker's search box
sel.trigger("wiktku:refresh");
ta.focus();
return;
}
// The noLang templates (ku-IPA, ku-kîte, zza-IPA, zza-kîte)
// auto-generate content from a Lua module reading the page
// title, so show a live preview and require confirmation
// before inserting.
var tplLabel = selectedOption.text();
$(this).val("");
sel.trigger("wiktku:refresh");
steps.pron.showTplPreview(stepDiv, ta, snippet, tplLabel);
});
wrap.append(sel).append(ta);
stepDiv.append(wrap);
steps.pron.buildIpaPalette(wrap, ta);
initSelects(wrap);
return ta;
},
// ── IPA character palette ──────────────────────────────────
// Modelled on fr.wiktionary's: every character is a button that
// drops itself in at the cursor. Two sub-tabs, because the full
// IPA chart is ~180 symbols and roughly twenty of them ever
// appear in Kurmancî — "IPA (kurdî)" opens by default and the
// full chart is one click away for loanwords and other
// languages. Grouped with a label down the left so it reads as
// a chart rather than one long line of glyphs.
IPA_KU: [
{ label: "Stres", chars: ["ˈ", "ˌ", "ː"] },
{ label: "Bêdeng", chars: ["d͡ʒ", "t͡ʃ", "ʒ", "ʃ", "ɫ", "ŋ", "ɾ", "r", "ʋ", "ħ", "ʕ", "ɣ", "χ", "ʔ"] },
{ label: "Bideng", chars: ["ɑː", "eː", "ɛ", "ɪ", "iː", "ɯ", "ʊ", "uː", "yː", "ø", "œ"] }
],
IPA_FULL: [
{ label: "Bêdeng 1", chars: ["m", "ɱ", "n", "ɳ", "ɲ", "ŋ", "ɴ", "p", "b", "t", "d", "t̪", "d̪", "ʈ", "ɖ", "c", "ɟ", "k", "ɡ", "q", "ɢ", "ʡ", "ʔ"] },
{ label: "Bêdeng 2", chars: ["ɸ", "β", "f", "v", "θ", "ð", "s", "z", "ʃ", "ʒ", "ʂ", "ʐ", "ɕ", "ʑ", "ç", "ʝ", "x", "ɣ", "χ", "ʁ", "ħ", "ʕ", "h", "ɦ", "ʜ", "ʢ", "ɧ"] },
{ label: "Bêdeng 3", chars: ["ʋ", "ɹ", "ɻ", "j", "ɥ", "ɰ", "ʍ", "w", "ʙ", "ⱱ", "ɾ", "ɽ", "r", "ʀ", "ɺ", "l", "ɫ", "ɬ", "ɮ", "ɭ", "ʎ", "ʟ"] },
{ label: "Bêdeng 4", chars: ["ɓ", "ɗ", "ᶑ", "ʄ", "ɠ", "ʛ", "ʘ", "ǀ", "ǃ", "ǂ", "ǁ"] },
{ label: "Bideng", chars: ["i", "y", "ɪ", "ʏ", "ɨ", "ʉ", "ɯ", "u", "ʊ", "e", "ø", "ɘ", "ɵ", "ɤ", "o", "ə", "ɚ", "ɛ", "œ", "ɜ", "ɝ", "ɞ", "ʌ", "ɔ", "æ", "ɶ", "ɐ", "a", "ɑ", "ɒ"] },
{ label: "Suprasegmental", chars: ["ˈ", "ˌ", "ː", "ˑ", "ʼ", "ˀ", "ˤ", "ᵝ", "ᵊ", "ˠ", "ʰ", "ʱ", "ʲ", "ˡ", "ⁿ", "ᵑ", "ʷ", "ᶣ", "˞", "‿"] },
{ label: "Dîakrîtîk", chars: ["̚", "̪", "̺", "̻", "̼", "̬", "̊", "̥", "̞", "̝", "̘", "̙", "̽", "̟", "̠", "̈", "̤", "̹", "̜", "̍", "̩", "̆", "̯", "̃", "̰", "͡", "͜"] },
{ label: "Ton", chars: ["̋", "́", "̄", "̀", "̏", "̌", "̂", "˥", "˦", "˧", "˨", "˩", "ꜛ", "ꜜ", "↗", "↘", "‖"] }
],
/** Builds the two-sub-tab character palette under the textarea. */
buildIpaPalette: function (wrap, ta) {
if (wrap.find(".wiktku-ipa-palette").length) return;
var panel = $("<div class='wiktku-ipa-panel'></div>").hide();
var toggle = $("<a href='#' class='wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Herfên IPAyê</span>"));
toggle.on("click", function (e) {
e.preventDefault();
var open = panel.is(":visible");
panel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
wrap.append($("<div class='wiktku-adv-toggle-row'></div>").append(toggle));
// Sub-tab bar, with the two template buttons pinned right.
var subTabs = $("<div class='wiktku-ipa-subtabs'></div>");
var kuTab = $("<span class='wiktku-ipa-subtab wiktku-tab-active'>IPA (kurdî)</span>");
var fullTab = $("<span class='wiktku-ipa-subtab'>IPA</span>");
subTabs.append(kuTab).append(fullTab);
var tplBtns = $("<span class='wiktku-ipa-tplbtns'></span>");
$.each([
{ label: "{{ku-IPA|+}}", snippet: "{{ku-IPA|}}", back: 2 },
{ label: "{{IPA|+}}", snippet: "{{IPA|ku|}}", back: 2 }
], function (_, t) {
var b = $("<button type='button' class='wiktku-ipa-tplbtn'></button>").text(t.label);
b.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], t.snippet);
// Park the cursor inside the braces, where the value goes.
var pos = ta[0].selectionStart - t.back;
ta[0].selectionStart = ta[0].selectionEnd = pos;
ta.focus();
});
tplBtns.append(b);
});
subTabs.append(tplBtns);
panel.append(subTabs);
/** One character key — shared by both the plain grid (every
* group's row shown at once) and the grouped grid (one
* group's row shown at a time, picked from a dropdown). */
function makeKeyBtn(ch) {
var btn = $("<button type='button' class='wiktku-ipa-key'></button>");
// A combining mark on its own renders as a stray
// accent floating over whatever precedes it, so
// show it on a dotted circle — but insert only
// the mark itself.
var combining = /^[̀-ͯ҃-҉]$/.test(ch);
btn.text(combining ? "◌" + ch : ch);
if (combining) btn.addClass("wiktku-ipa-key-combining");
btn.attr("title", ch);
btn.on("click", function (e) {
e.preventDefault();
insertAtCursor(ta[0], ch);
ta.focus();
});
return btn;
}
function buildGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid'></div>");
$.each(groups, function (_, g) {
var row = $("<div class='wiktku-ipa-row'></div>");
row.append($("<span class='wiktku-ipa-rowlabel'></span>").text(g.label));
var keys = $("<span class='wiktku-ipa-keys'></span>");
$.each(g.chars, function (_, ch) { keys.append(makeKeyBtn(ch)); });
row.append(keys);
grid.append(row);
});
return grid;
}
// The full IPA chart runs to ~180 symbols across 8 groups —
// too long to show all at once, so a dropdown picks one group
// (Bêdeng 1, Bêdeng 2, …) and only that group's keys show.
function buildGroupedGrid(groups) {
var grid = $("<div class='wiktku-ipa-grid wiktku-ipa-grid--grouped'></div>");
var picker = $("<select class='wiktku-ipa-group-select wiktku-plain-select wiktku-start-narrow'></select>");
$.each(groups, function (i, g) {
picker.append($("<option></option>").val(i).text(g.label));
});
var keys = $("<span class='wiktku-ipa-keys'></span>");
function renderGroup(idx) {
keys.empty();
var g = groups[idx];
if (!g) return;
$.each(g.chars, function (_, ch) { keys.append(makeKeyBtn(ch)); });
}
picker.on("change", function () { renderGroup(this.selectedIndex); });
renderGroup(0);
grid.append($("<div class='wiktku-ipa-group-row'></div>").append(picker));
grid.append($("<div class='wiktku-ipa-row'></div>").append(keys));
return grid;
}
var kuGrid = buildGrid(steps.pron.IPA_KU);
var fullGrid = buildGroupedGrid(steps.pron.IPA_FULL).hide();
panel.append(kuGrid).append(fullGrid);
kuTab.on("click", function () {
kuTab.addClass("wiktku-tab-active"); fullTab.removeClass("wiktku-tab-active");
kuGrid.show(); fullGrid.hide();
});
fullTab.on("click", function () {
fullTab.addClass("wiktku-tab-active"); kuTab.removeClass("wiktku-tab-active");
fullGrid.show(); kuGrid.hide();
});
panel.addClass("wiktku-ipa-palette");
wrap.append(panel);
},
/** Inline preview + confirm panel for a "noLang" (auto-content)
* pronunciation template — fetches what the snippet expands to
* for this page and only inserts it if the user confirms. */
showTplPreview: function (stepDiv, ta, snippet, tplLabel) {
// Only one preview panel open at a time.
stepDiv.find(".wiktku-pron-preview").remove();
var panel = $("<div class='wiktku-pron-preview'></div>");
panel.append($("<div class='wiktku-pron-preview-label'></div>")
.text("Pêşdîtina " + tplLabel + ":"));
var body = $("<div class='wiktku-pron-preview-body'></div>").text("Tê barkirin…");
panel.append(body);
var actions = $("<div class='wiktku-pron-preview-actions'></div>");
var confirmBtn = $("<button type='button' class='wiktku-pron-preview-confirm'>✓ Tevlî bike</button>")
.prop("disabled", true);
var cancelBtn = $("<button type='button' class='wiktku-pron-preview-cancel'>✕ Betal bike</button>");
actions.append(confirmBtn).append(cancelBtn);
panel.append(actions);
ta.before(panel);
confirmBtn.on("click", function () {
insertLineSnippet(ta[0], snippet);
panel.remove();
ta.focus();
});
cancelBtn.on("click", function () {
panel.remove();
});
// action=parse, not action=expandtemplates. expandtemplates
// returns *expanded wikitext*, so the preview read as
// [[IPA]]<sup>([[Wîkîferheng:IPA kurmancî|kilîd]])</sup>:
// <span class="IPA">/-{kshhe}-/</span>[[Kategorî:…]] — which
// is exactly what the person is trying to avoid having to
// read. parse hands back the rendered HTML the page will
// actually show.
//
// Drop the leading "* " first: it is wikitext list syntax
// wrapping the template, and keeping it would render the
// preview as a bulleted list rather than the line itself.
new mw.Api().post({
action: "parse",
title: pageName,
text: snippet.replace(/^\*\s*/, ""),
prop: "text",
wrapoutputclass: "",
disablelimitreport: 1,
disableeditsection: 1,
contentmodel: "wikitext",
formatversion: 2
}).done(function (data) {
var html = (data && data.parse && data.parse.text) || "";
if ($.trim($("<div>").html(html).text())) {
body.html(html);
// Category links render as nothing but still exist;
// and any real link inside a preview should not
// navigate away from a half-filled form.
body.find("a").attr("target", "_blank").attr("rel", "noopener");
} else {
body.text("(vala — ji bo vê rûpelê tiştek nehate afirandin)");
}
confirmBtn.prop("disabled", false);
}).fail(function () {
body.text("Pêşdîtin nehate barkirin — tu dikarî hîn jî wê tevlî bikî.");
confirmBtn.prop("disabled", false);
});
},
output: function () {
var text = $.trim($("#wiktku-start-pron .wiktku-pron-text").val());
if (!text) return "";
var lines = text.split("\n");
var out = $.map(lines, function (line) {
line = $.trim(line);
if (!line) return undefined;
return line;
}).join("\n");
// "===" heading under Ziman and above the POS sections; the
// field is shared across sections, so it's emitted once by
// getWikitext() rather than per-POS-section (would otherwise
// print the pronunciation twice for a two-POS entry).
return "\n=== Bilêvkirin ===\n" + out + "\n";
} },
pos: {
id: "pos",
required: true,
name: "Cureyê rêzimanî",
question_fmt: 'Cureyê rêzimanî yê "' + pageName + '" çi ye?',
visible: function (values) {
return values.lang;
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (posChoices, posExamples).
choices: [],
// Definition of whichever cureyê rêzimanî is currently chosen —
// used to be the Mane textarea's placeholder, which mixed up "what
// this grammatical category means" with "what to type here" (see
// buildSenseBlock's placeholder for the latter, now generic).
// Shown as plain text right under the dropdown instead (see the
// .wiktku-start-pos-example build/refresh() above).
posExamples: {},
example: function (values) {
var pos = values.pos || "";
return steps.pos.posExamples[pos] ||
"Cureyê rêzimanî yê vê kelîmeyê hilbijêre — navdêr, lêker, rengdêr hwd.";
},
output: function (values) {
var pos = values.pos || "";
var capitalizedPos = capitalize(pos);
// "form" opt-param (checked in posparams) marks this as a
// form-of/inflected entry — the section title becomes
// "Formeke <pos>ê" instead of the plain pos name. The
// template call itself still uses the unchanged pos name.
var isForm = $("#wiktku-posparams-opt-form").is(":checked");
var header = isForm
? "\n=== Formeke " + pos + "ê ===\n"
: "\n=== " + capitalizedPos + " ===\n";
// Image line (between header and template call)
var imageLine = steps.resim.output(values);
// Read the live wikitext from the posparams textarea.
// Fall back to a plain {{pos|lang}} if the step hasn't been built yet.
var tplLine = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
if (!tplLine) {
tplLine = "{{" + pos + "|" + (values.lang || "") + "}}";
}
return header + imageLine + tplLine + "\n";
}
},
// cinsiyet (gender) — shown for navdêr/serenav only, with choices built
// from metadata[lang].g since not every language has grammatical gender.
cinsiyet: {
id: "cinsiyet",
name: "cinsiyet",
question_fmt: 'Cinsiyeta "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
// Single source of truth for the selected gender code. The visible
// dropdown lives inside the posparams step (see steps.posparams),
// but it must always read/write THIS hidden field — not a field of
// its own — since getValuesForStep("cinsiyet") reads from here.
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-cinsiyet-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// jimar (grammatical number) — same hidden-field pattern as cinsiyet
// above, but writes into its own j= template parameter instead of
// sharing c=. Shown for navdêr/serenav only, with the same choices
// for every language — data.json's numberLabels (see syncHelpers).
jimar: {
id: "jimar",
name: "jimar",
question_fmt: 'Jimara "' + pageName + '" çi ye?',
visible: function () { return false; }, // handled inside posparams
build: function (stepDiv) {
var field = stepDiv.find(".wiktku-start-step-field");
if (field.length) return field;
field = $("<input type='hidden'>")
.addClass("wiktku-start-step-field")
.attr("id", "wiktku-start-jimar-field")
.val("");
stepDiv.append(field);
return field;
},
output: function () { return ""; } // consumed by posparams/pos.output
},
// Lêker transitivity — hidden step, handled inside posparams
lektur: {
id: "lektur",
name: "Cureyê lêkerê (fiîlê)",
question_fmt: "Cureyê lêkerê (fiîlê) çi ye?",
visible: function () { return false; },
choices: [
{ code: "gh", label: "gerguhêz" },
{ code: "x-gh", label: "xwerû-gerguhêz" },
{ code: "p-gh", label: "pêkhatî-gerguhêz" },
{ code: "h-gh", label: "hevedudanî-gerguhêz" },
{ code: "b-gh", label: "biwêjî-gerguhêz" },
{ code: "ngh", label: "negerguhêz" },
{ code: "x-ngh", label: "xwerû-negerguhêz" },
{ code: "p-ngh", label: "pêkhatî-negerguhêz" },
{ code: "h-ngh", label: "hevedudanî-negerguhêz" },
{ code: "b-ngh", label: "biwêjî-negerguhêz" } ],
output: function () { return ""; }
},
// tr — hidden step, handled inside posparams
tr: {
id: "tr",
name: "transliterasyon",
question_fmt: "Transliterasyona vê kelîmeyê",
multiple: false,
narrow: true,
visible: function () { return false; }
},
posparams: {
id: "posparams",
// Fused onto the "pos" card visually (see #wiktku-start-posparams
// in styles.css) so the two read as one question — the
// "taybetmendî" name above is a no-CSS/no-JS fallback only,
// since its h3 is hidden by CSS, and this step has no question
// text of its own either: each field below is already
// self-labelled, and repeating it would just echo "Cureyê
// rêzimanî" one card up (see the pos step above, fused flush
// against this one — no gap, no duplicate heading).
name: "taybetmendî",
question_fmt: "",
visible: function (values) {
return !!(values.lang && values.pos);
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-posparams-wrap").length) return null;
var wrap = $("<div class='wiktku-posparams-wrap'></div>");
var helpers = $("<div class='wiktku-posparams-helpers'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-posparams-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-posparams-text")
.attr("rows", 2);
var _rebuilding = false;
function safeRebuild() {
if (_rebuilding) return;
_rebuilding = true;
steps.posparams.rebuildTa(stepDiv);
_rebuilding = false;
refresh();
}
// Generic text-input row factory
function makeTextRow(key, labelText, placeholder) {
var row = $("<div class='wiktku-posparams-row'></div>")
.attr("id", "wiktku-posparams-row-" + key).hide();
var inp = $("<input type='text'>")
.addClass("wiktku-start-narrow wiktku-posparams-field")
.attr("data-pp-key", key)
.attr("placeholder", placeholder || "");
inp.on("input", safeRebuild);
row.append($("<label></label>").text(labelText + ": ")).append(inp);
return row;
}
// cinsiyet
var cinsiyetRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-c'></div>")
.append($("<label></label>").text("Cinsiyet: ")).hide();
var cinsiyetSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-cinsiyet-sel")
.append($("<option value=''>— hilbijêre —</option>"));
// Writes into the real, shared #wiktku-start-cinsiyet-field (owned by
// steps.cinsiyet) rather than a field of its own — see the comment
// on steps.cinsiyet.build for why that matters.
cinsiyetSel.on("change", function () {
$("#wiktku-start-cinsiyet-field").val($(this).val()).trigger("change");
safeRebuild();
});
cinsiyetRow.append(cinsiyetSel);
// jimar (number) — lives in "Tercîhên pêşketî" (rarely needed),
// writes into the shared hidden #wiktku-start-jimar-field, into
// its own j= param instead of sharing c=. Options are built in
// syncHelpers once the language's number codes are known.
var jimarRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-jimar'></div>")
.append($("<label></label>").text("Jimar: ")).hide();
var jimarSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-jimar-sel")
.append($("<option value=''>— hilbijêre —</option>"));
jimarSel.on("change", function () {
$("#wiktku-start-jimar-field").val($(this).val()).trigger("change");
safeRebuild();
});
jimarRow.append(jimarSel);
// lektur: two plain questions (transitivity + structural form)
// instead of one 10-option dropdown. Emitted code: base alone
// ("gh"), or prefix + "-" + base ("x-gh").
var lekturRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lektur'></div>")
.append($("<label></label>").text("Lêker derbasdar e? ")).hide();
var lekturSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lektur-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='gh'>gerguhêz (bireser digire)</option>"))
.append($("<option value='ngh'>negerguhêz (bireser nagire)</option>"));
lekturSel.on("change", safeRebuild);
lekturRow.append(lekturSel);
// Second half of the same question — the structural form.
// Blank = xwerû/sade, which is the common case; shown
// alongside lektur (not gated on it being answered first —
// rebuildTa() only uses this once lektur has a value anyway).
var lekturFormRow = $("<div class='wiktku-posparams-row' id='wiktku-posparams-row-lekturform'></div>")
.append($("<label></label>").text("Şiklê lêkerê: ")).hide();
var lekturFormSel = $("<select></select>")
.addClass("wiktku-plain-select wiktku-start-narrow wiktku-posparams-lekturform-sel")
.append($("<option value=''>— hilbijêre —</option>"))
.append($("<option value='x'>xwerû</option>"))
.append($("<option value='p'>pêkhatî</option>"))
.append($("<option value='h'>hevedudanî</option>"))
.append($("<option value='b'>biwêjî</option>"));
lekturFormSel.on("change", safeRebuild);
lekturFormRow.append(lekturFormSel);
helpers.append(cinsiyetRow).append(lekturRow).append(lekturFormRow);
// ── Everything below here is "Tercîhên pêşketî" ──────────────
// These rows live inside a collapsed panel, so the default
// view of this card is two dropdowns instead of twenty-odd fields.
// Transliteration is a primary field, not an advanced one, for
// any language not written in Latin script: for those, an
// entry without it is incomplete. syncHelpers already gates
// the row on needsTransliteration(lang), so for Kurmancî and
// other Latin-script languages it simply never appears.
helpers.append(makeTextRow("tr", "Transliterasyon (tr)", "Transliterasyon…"));
// fsubj/fdbê/fniha/fborî: the actual conjugated forms (as
// opposed to niha/borî/subj below, which are the bare roots
// the Lua module conjugates from) — primary fields for
// lêker, not tucked behind "Tercîhên pêşketî", same
// reasoning as tr above. fniha/fborî show for every lêker
// language; fdbê (forma dema bê) only for ku, and fsubj
// (fermanî) only outside ku — see the isLeker/isKuLeker/
// isLekerSubj toggles in syncHelpers.
var tewandinHeading = $("<div class='wiktku-posparams-subheading' id='wiktku-posparams-tewandin-heading'></div>")
.text("Tewandina lêkerê").hide();
helpers.append(tewandinHeading);
var fnihaRow = makeTextRow("fniha", "Forma dema niha (fniha)", "Forma dema niha…");
var fborîRow = makeTextRow("fborî", "Forma dema borî (fborî)", "Forma dema borî…");
var fdbêRow = makeTextRow("fdbê", "Forma dema bê (fdbê)", "Forma dema bê…");
var fsubjRow = makeTextRow("fsubj", "Forma dema fermanî (fsubj)", "Forma dema fermanî…");
// ez/min hint (Kurmancî only, see syncHelpers): Kurmanji's split
// ergativity puts the present-tense subject and (for
// negerguhêz verbs) the past-tense subject in the nominative
// ("ez"), but a gerguhêz verb's past-tense subject is oblique
// ("min") — so fborî's hint tracks the transitivity answer
// above while fniha's stays fixed at "ez". fdbê gets its own
// fixed "ew ê" hint instead — third person, so it doesn't
// read like fsubj/fermanî's implied second-person "tu".
// Wrapped together with the box in its own flex line
// (.wiktku-posparams-field-line) so the hint sits right
// before the box even when the row itself stacks
// label-over-field (desktop grid layout).
function addPronounHint(row, hintId, hintText) {
var inp = row.find(".wiktku-posparams-field");
var hint = $("<span class='wiktku-posparams-pronoun-hint' id='" + hintId + "'></span>").text(hintText || "").hide();
var line = $("<span class='wiktku-posparams-field-line'></span>");
inp.before(line);
line.append(hint).append(inp);
}
addPronounHint(fnihaRow, "wiktku-posparams-fniha-hint", "ez");
addPronounHint(fborîRow, "wiktku-posparams-fborî-hint", "");
addPronounHint(fdbêRow, "wiktku-posparams-fdbê-hint", "ew ê");
helpers.append(fnihaRow);
helpers.append(makeTextRow("fnihatr", "Translit. forma dema niha", "Transliterasyona forma dema niha…"));
helpers.append(fborîRow);
helpers.append(makeTextRow("fborîtr", "Translit. forma dema borî", "Transliterasyona forma dema borî…"));
helpers.append(fdbêRow);
helpers.append(makeTextRow("fdbêtr", "Translit. forma dema bê", "Transliterasyona forma dema bê…"));
helpers.append(fsubjRow);
helpers.append(makeTextRow("fsubjtr", "Translit. forma dema fermanî", "Transliterasyona forma dema fermanî…"));
var advPanel = $("<div class='wiktku-posparams-adv-panel'></div>").hide();
advPanel
.append(jimarRow)
.append(makeTextRow("cuda", "nîşankirina alternatîv", "cuda…"))
.append(makeTextRow("ts", "Transkrîpsiyon (ts)", "Transkrîpsiyon…"))
.append(makeTextRow("m", "Forma mê (m)", "Forma mê…"))
.append(makeTextRow("mtr", "Translit. mê (mtr)", "Transliterasyona mê…"))
.append(makeTextRow("n", "Forma nêr (n)", "Forma nêr…"))
.append(makeTextRow("ntr", "Translit. nêr (ntr)", "Transliterasyona nêr…"))
.append(makeTextRow("p", "Forma pirjimar (p)", "Forma pirjimar…"))
.append(makeTextRow("ptr", "Translit. pirjimar", "Transliterasyona pirjimar…"))
.append(makeTextRow("y", "Forma yekjimar (y)", "Forma yekjimar…"))
.append(makeTextRow("ytr", "Translit. yekjimar", "Transliterasyona yekjimar…"))
.append(makeTextRow("sûp", "Forma sûperlatîv (sûp)", "Forma sûperlatîv…"))
.append(makeTextRow("sûptr", "Translit. sûperlatîv", "Transliterasyona sûperlatîv…"))
.append(makeTextRow("komp", "Forma komparatîv (komp)", "Forma komparatîv…"))
.append(makeTextRow("komptr", "Translit. komparatîv", "Transliterasyona komparatîv…"))
.append(makeTextRow("gir", "Forma girdêk (gir)", "gir=…"))
.append(makeTextRow("girtr", "Translit. gir", "girtr=…"))
.append(makeTextRow("hûr", "Forma hûrek (hûr)", "hûr=…"))
.append(makeTextRow("hûrtr", "Translit. hûr", "hûrtr=…"))
.append(makeTextRow("hanja", "Forma hanja", "hanja=…"))
.append(makeTextRow("hanjaTr","Translit. hanja", "hanjaTr=…"))
// Lêker rehên demê (only shown when pos is lêker — see syncHelpers)
.append(makeTextRow("niha", "Rehê dema niha (niha)", "Rehê dema niha…"))
.append(makeTextRow("nihatr", "Translit. rehê dema niha", "Transliterasyona rehê dema niha…"))
.append(makeTextRow("borî", "Rehê dema borî (borî)", "Rehê dema borî…"))
.append(makeTextRow("borîtr", "Translit. rehê dema borî", "Transliterasyona rehê dema borî…"))
.append(makeTextRow("subj", "Rehê dema fermanî (subj)", "Rehê dema fermanî…"))
.append(makeTextRow("subjtr", "Translit. rehê dema fermanî", "Transliterasyona rehê dema fermanî…"));
// Optional boolean params — nested inside the advanced panel
// (they were already the most niche thing on the card).
var optPanel = $("<div class='wiktku-posparams-opt-panel'></div>");
$.each([
{ key: "form", label: "form — ev made forma tewandî ye" },
{ key: "formaalt", label: "formaalt — vê madeyê bike forma alternatîv" },
{ key: "nekatcure", label: "nekatcure — sinifa rêzimanî kategorîze neke" },
{ key: "nekatcins", label: "nekatcins — kategoriya cinsiyetê neyîne" },
{ key: "nekatçendterm", label: "nekatçendterm — kategoriya çend-term neyîne" },
{ key: "serlînkneke", label: "serlînkneke — sernavê madeyê lînk neke" },
{ key: "nekatpalîndrom", label: "nekatpalîndrom — kategoriya palîndromê neyîne" },
{ key: "neformtr", label: "neformtr — transliterasyona forman neyîne" }
], function (_, p) {
var cb = $("<input type='checkbox'>")
.attr("id", "wiktku-posparams-opt-" + p.key).val(p.key).on("change", safeRebuild);
var lbl = $("<label></label>").attr("for", "wiktku-posparams-opt-" + p.key).text(" " + p.label);
optPanel.append($("<div class='wiktku-posparams-opt-row'></div>").append(cb).append(lbl));
});
advPanel.append(optPanel);
var optToggle = $("<a href='#' class='wiktku-posparams-opt-toggle wiktku-adv-toggle'></a>")
.attr("aria-expanded", "false")
.append($("<span class='wiktku-adv-toggle-chevron'>▾</span>"))
.append($("<span class='wiktku-adv-toggle-text'>Tercîhên pêşketî</span>"));
optToggle.on("click", function (e) {
e.preventDefault();
var open = advPanel.is(":visible");
advPanel.toggle(!open);
$(this).attr("aria-expanded", open ? "false" : "true")
.toggleClass("wiktku-adv-open", !open);
});
helpers.append($("<div class='wiktku-adv-toggle-row'></div>")
.append(optToggle));
helpers.append(advPanel);
wrap.append(helpers).append(ta);
// Typing directly in textarea keeps cinsiyet/jimar hidden
// fields in sync, but does NOT call refresh() — preserves
// manual edits.
ta.on("input", function () {
var text = $(this).val();
var cMatch = text.match(/\|c=([^|}\s]+)/);
$("#wiktku-start-cinsiyet-field").val(cMatch ? cMatch[1] : "");
var jMatch = text.match(/\|j=([^|}\s]+)/);
$("#wiktku-start-jimar-field").val(jMatch ? jMatch[1] : "");
});
stepDiv.append(wrap);
return null;
},
/** Reads all helper widget values and rewrites the textarea.
* Called only by safeRebuild (user interaction) — never by syncHelpers,
* so manual textarea edits are never overwritten by a refresh cycle. */
rebuildTa: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
if (!pos || !lang) return;
var cinsiyet = stepDiv.find(".wiktku-posparams-cinsiyet-sel").val() || "";
var jimar = stepDiv.find(".wiktku-posparams-jimar-sel").val() || "";
// Two dropdowns, one code: "gh" / "ngh" on their own, or
// prefixed with the structural form ("x-gh", "b-ngh", …).
var lekturBase = stepDiv.find(".wiktku-posparams-lektur-sel").val() || "";
var lekturForm = stepDiv.find(".wiktku-posparams-lekturform-sel").val() || "";
var lektur = lekturBase ? (lekturForm ? lekturForm + "-" + lekturBase : lekturBase) : "";
function v(key) {
return $.trim(stepDiv.find(".wiktku-posparams-field[data-pp-key='" + key + "']").val());
}
var out = "{{" + pos + "|" + lang;
if (pos === "navdêr" || pos === "serenav") {
out += cinsiyet ? "|c=" + cinsiyet : (lang === "ku" ? "|c=?" : "");
// Number is its own j= param, separate from c= (which
// holds gender, including compounds like m-p/n-p).
if (jimar) out += "|j=" + jimar;
} else if (/^lêker/.test(pos) && lektur) {
out += "|c=" + lektur;
}
if (v("cuda")) out += "|" + v("cuda");
if (v("tr")) out += "|tr=" + v("tr");
if (v("ts")) out += "|ts=" + v("ts");
$.each(["m","n","p","y"], function (_, k) {
if (v(k)) out += "|" + k + "=" + v(k);
if (v(k+"tr")) out += "|" + k + "tr=" + v(k+"tr");
});
if (v("sûp")) out += "|sûp=" + v("sûp");
if (v("sûptr")) out += "|sûptr=" + v("sûptr");
if (v("komp")) out += "|komp=" + v("komp");
if (v("komptr")) out += "|komptr=" + v("komptr");
if (v("gir")) out += "|gir=" + v("gir");
if (v("girtr")) out += "|girtr=" + v("girtr");
if (v("hûr")) out += "|hûr=" + v("hûr");
if (v("hûrtr")) out += "|hûrtr=" + v("hûrtr");
if (v("hanja")) out += "|hanja=" + v("hanja");
if (v("hanjaTr")) out += "|hanjaTr=" + v("hanjaTr");
if (v("niha")) out += "|niha=" + v("niha");
if (v("nihatr")) out += "|nihatr=" + v("nihatr");
if (v("borî")) out += "|borî=" + v("borî");
if (v("borîtr")) out += "|borîtr=" + v("borîtr");
if (v("subj")) out += "|subj=" + v("subj");
if (v("subjtr")) out += "|subjtr=" + v("subjtr");
if (v("fniha")) out += "|fniha=" + v("fniha");
if (v("fnihatr")) out += "|fnihatr=" + v("fnihatr");
if (v("fborî")) out += "|fborî=" + v("fborî");
if (v("fborîtr")) out += "|fborîtr=" + v("fborîtr");
if (v("fdbê")) out += "|fdbê=" + v("fdbê");
if (v("fdbêtr")) out += "|fdbêtr=" + v("fdbêtr");
if (v("fsubj")) out += "|fsubj=" + v("fsubj");
if (v("fsubjtr")) out += "|fsubjtr=" + v("fsubjtr");
stepDiv.find(".wiktku-posparams-opt-panel input:checked").each(function () {
var key = $(this).val();
// "form" marks this entry as a form-of/inflected entry — it
// takes "erê" rather than the plain "1" the other boolean
// opt params use.
out += "|" + key + "=" + (key === "form" ? "erê" : "1");
});
out += "}}";
stepDiv.find(".wiktku-posparams-text").val(out);
$("#wiktku-start-cinsiyet-field").val(cinsiyet);
$("#wiktku-start-jimar-field").val(jimar);
},
/** Called from refresh() — controls row visibility only.
* Does NOT rewrite textarea so user edits are preserved.
* Rebuilds only when textarea is empty (first pos selection). */
syncHelpers: function (stepDiv) {
var values = getValues();
var pos = values.pos || "";
var lang = values.lang || "";
var needsTr = needsTransliteration(lang);
var isNom = pos === "navdêr" || pos === "serenav" || pos === "rengdêr";
var isAdj = pos === "rengdêr";
var isTîp = pos === "tîp";
var isJa = lang === "ja";
var isLeker = /^lêker/.test(pos);
// Kurmancî verbs are conjugated from the present/past roots
// alone — there's no separate subjunctive root the way some
// other languages have — so this pair of fields is noise for
// lang=ku specifically, unlike niha/borî which every lêker
// language needs.
var isLekerSubj = isLeker && lang !== "ku";
var isKuLeker = isLeker && lang === "ku";
// metadata[lang].g lists gender codes (and gender+plural
// compounds like m-p/n-p, which aren't NUMBER_LABELS keys) —
// genuinely per-language, so Cîns only offers whatever this
// language's own metadata defines (see isNumberCode, which
// filters out any number code that ended up in there too).
// Jimar, by contrast, is the same set of grammatical-number
// codes for every language — data.json's numberLabels.
var meta = metadata[lang];
var allCodes = (meta && meta.g) || [];
var genderCodes = $.grep(allCodes, function (c) { return !isNumberCode(c); });
var jimarCodes = Object.keys(NUMBER_LABELS);
var showC = (pos === "navdêr" || pos === "serenav") && genderCodes.length > 0;
var cRow = stepDiv.find("#wiktku-posparams-row-c");
cRow.toggle(showC);
if (showC) {
var sel = cRow.find(".wiktku-posparams-cinsiyet-sel");
// Prefer the select's own current value (normal in-session
// use); fall back to the shared hidden field, which is how
// a cache/section restore communicates a saved gender
// before this select has any <option>s to hold it.
var prev = sel.val() || $("#wiktku-start-cinsiyet-field").val() || "";
sel.find("option[value!='']").remove();
$.each(genderCodes, function (_, code) {
sel.append($("<option></option>").val(code).text(genderLabel(code)));
});
if (prev) sel.val(prev);
}
var showJimar = (pos === "navdêr" || pos === "serenav") && jimarCodes.length > 0;
var jimarRowEl = stepDiv.find("#wiktku-posparams-row-jimar");
jimarRowEl.toggle(showJimar);
if (showJimar) {
var jimarSelEl = jimarRowEl.find(".wiktku-posparams-jimar-sel");
var prevJimar = jimarSelEl.val() || $("#wiktku-start-jimar-field").val() || "";
jimarSelEl.find("option[value!='']").remove();
$.each(jimarCodes, function (_, code) {
var def = NUMBER_LABELS[code] || {};
var opt = $("<option></option>").val(code).text(numberOptionLabel(code));
// The explanation (cat, e.g. "dualia tantum") shows as a
// hover tooltip — <option> can't hold richer markup.
if (def.cat) opt.attr("title", def.cat);
jimarSelEl.append(opt);
});
if (prevJimar) jimarSelEl.val(prevJimar);
}
stepDiv.find("#wiktku-posparams-row-lektur").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-lekturform").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-tr").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-ts").toggle(needsTr);
stepDiv.find("#wiktku-posparams-row-m").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-mtr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-n").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ntr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-p").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ptr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-y").toggle(isNom);
stepDiv.find("#wiktku-posparams-row-ytr").toggle(isNom && needsTr);
stepDiv.find("#wiktku-posparams-row-sûp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-sûptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-komp").toggle(isAdj);
stepDiv.find("#wiktku-posparams-row-komptr").toggle(isAdj && needsTr);
stepDiv.find("#wiktku-posparams-row-gir").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-girtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hûr").toggle(isTîp);
stepDiv.find("#wiktku-posparams-row-hûrtr").toggle(isTîp && needsTr);
stepDiv.find("#wiktku-posparams-row-hanja").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-hanjaTr").toggle(isJa);
stepDiv.find("#wiktku-posparams-row-niha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-nihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-borî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-borîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-subj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-subjtr").toggle(isLekerSubj && needsTr);
// fniha/fborî are shown for every lêker language. fsubj
// (fermanî) is skipped for ku, same as subj/subjtr above —
// fdbê (forma dema bê) covers that slot in Kurmancî's own
// conjugation instead.
stepDiv.find("#wiktku-posparams-tewandin-heading").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fniha").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fnihatr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fborî").toggle(isLeker);
stepDiv.find("#wiktku-posparams-row-fborîtr").toggle(isLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fdbê").toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-row-fdbêtr").toggle(isKuLeker && needsTr);
stepDiv.find("#wiktku-posparams-row-fsubj").toggle(isLekerSubj);
stepDiv.find("#wiktku-posparams-row-fsubjtr").toggle(isLekerSubj && needsTr);
// ez/min hints only make sense for Kurmancî itself — fborî's
// hint follows the transitivity answer (gerguhêz → "min",
// otherwise "ez"); fniha's is always "ez"; fdbê's is always
// "ew ê" (isKuLeker already gates fdbê to ku alone).
stepDiv.find("#wiktku-posparams-fniha-hint").toggle(isKuLeker);
var lekturVal = stepDiv.find(".wiktku-posparams-lektur-sel").val();
stepDiv.find("#wiktku-posparams-fborî-hint")
.text(lekturVal === "gh" ? "min" : "ez")
.toggle(isKuLeker);
stepDiv.find("#wiktku-posparams-fdbê-hint").toggle(isKuLeker);
if (!$.trim(stepDiv.find(".wiktku-posparams-text").val())) {
steps.posparams.rebuildTa(stepDiv);
}
},
output: function () { return ""; } // pos.output reads the textarea directly
},
dfn: {
id: "dfn",
name: "Mane",
// Field types that live behind "Malûmatên din". Kept as one
// list so the collapse CSS, the count badge and the allGroups
// adv: flags below can't drift apart.
ADV_SUBGROUPS: [
"wiktku-dfn-etiket",
"wiktku-dfn-kategori",
"wiktku-dfn-alternativ",
"wiktku-dfn-koordine",
"wiktku-dfn-jornav",
"wiktku-dfn-jernav"
],
question_fmt: function (values) {
var base = "Maneya kelîmeyê binivîse. Mane otomatîk tê lînkkirin!";
if (values.lang === "ku") {
return base + " Ji bo tercimeyan beşa 'Werger' bi kar bîne!";
}
return base;
},
multiple: true,
ordered: true,
rows: 3,
visible: function (values) {
return values.lang && values.pos;
},
// Builds one sense block (<li>): mane textarea, nimûne list, and
// sub-lists for alternatîv/hevmane/dijmane/etc.
buildSenseBlock: function () {
var block = $("<li class='wiktku-dfn-sense'></li>");
// — Mane textarea (declared first so the template handler can reference it) —
var dfnArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-mane-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-start-step-field wiktku-dfn-mane")
.attr("rows", 6)
.attr("placeholder", "Maneya vê kelîmeyê binivîse…");
// Deliberately does nothing on keystrokes. The only thing a live
// Mane edit needs to propagate is the Werger/Bi zaravayên din
// gloss list (glossList()/build() re-read every sense in the
// DOM, and get slow with more than one sense around) — mane
// itself isn't a required step, so nothing else in refresh()
// depends on it changing mid-keystroke. Deferred to blur
// instead; getWikitext()/output() always re-read the DOM fresh
// regardless, so the final wikitext is correct either way.
dfnArea.on("blur", function () { refresh(); });
// — Tercime + Îzah boxes (non-ku alternative to the plain mane box) —
// Non-ku entries get a direct translation plus a {{îzah|…}}-wrapped
// gloss instead of a Kurdish-style gloss. Rather than teach every
// consumer (getSenses/getOutput/linkify) a second data shape, these
// boxes just write their combined text into dfnArea and re-fire its
// "input" event, so dfnArea stays the single source of truth.
var tercimeArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-tercime-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-tercime")
.attr("rows", 2)
.attr("placeholder", "Tercimeyên rasterast ên kurmancî… Ji bo school-a îngilîzî li vir 'mekteb, medrese' binivîse");
var izahArea = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-izah-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", "wiktku-dfn-subfield wiktku-dfn-izah")
.attr("rows", 3)
.attr("placeholder", "Îzaha kurmancî… Ji bo school-a îngilîzî li vir 'cihê tehsîlgirtinê' binivîse");
var nonKuWrap = $("<div class='wiktku-dfn-nonku-wrap'></div>")
.append(tercimeArea).append(izahArea)
.hide();
function syncNonKuToMane() {
var t = $.trim(tercimeArea.val());
var iz = $.trim(izahArea.val());
// if tercime is blank, use îzah as definition
var combined = t ? (iz ? t + " {{îzah|" + iz + "}}" : t) : iz;
dfnArea.val(combined);
// Re-fires onDfnInput, as if this were typed directly into
// the mane textarea.
dfnArea.trigger("input");
}
tercimeArea.on("input", syncNonKuToMane);
izahArea.on("input", syncNonKuToMane);
// Same deferred-refresh treatment as dfnArea itself — these two
// feed into it (see syncNonKuToMane above).
tercimeArea.on("blur", function () { refresh(); });
izahArea.on("blur", function () { refresh(); });
// Switches between the plain mane box and the tercime+îzah pair —
// only for non-ku entries with no tplSelect selected.
function updateManeMode() {
var lang = getValues().lang || "";
var isTpl = !!tplSelect.val();
if (lang && lang !== "ku" && !isTpl) {
dfnArea.hide();
nonKuWrap.show();
} else {
nonKuWrap.hide();
if (!isTpl) dfnArea.show();
}
}
// Store on the block so refresh() can re-sync every block when
// lang changes, and restoreDfn() can re-sync after restoring.
block.data("updateManeMode", updateManeMode);
// — Template selector (optional — replaces mane when chosen) —
// Template definitions loaded from [[MediaWiki:Gadget-start.js/data.json]].
var tplDefs = steps.dfn.tplDefs || {};
// Cache of all non-placeholder option elements, so updateTplOptions
// can detach/re-attach them without losing the full list.
var _allTplOptions = [];
var tplSelect = $(mw.html.element("select", { "class": "wiktku-dfn-tpl-select" }));
tplSelect.attr("data-placeholder", "Li şablonekê bigere");
tplSelect.append($("<option value=''>— yan jî şablonek hilbijêre —</option>"));
$.each(tplDefs, function (name, tpl) {
// Shows "name — tpl.desc" when a tplDefs entry has a desc
// in data.json, same pattern as steps.pron's template picker.
var opt = $("<option></option>")
.val(name)
.text(tpl.desc ? name + " — " + tpl.desc : name)
.attr("data-langs", tpl.langs ? tpl.langs.join(",") : "")
.attr("data-nolang", tpl.noLang ? "1" : "");
tplSelect.append(opt);
_allTplOptions.push(opt[0]);
});
// Same status-dot / 🔗 / ⓘ row treatment as every other
// template picker. This one carries the bare name in value=,
// so the name mapper is the identity rather than a snippet
// parse; descriptions come from each template's TemplateData.
markTemplateOptions(tplSelect, function (opt) { return opt.val(); });
function updateTplOptions() {
var lang = getValues().lang || "";
var currentVal = tplSelect.val();
var currentStillValid = false;
// Detach all non-placeholder options, then re-attach only the visible
// ones — native <select> won't reliably hide options any other way.
tplSelect.find("option[value!='']").detach();
$.each(_allTplOptions, function (_, opt) {
var allowed = $(opt).attr("data-langs");
var visible = !allowed || allowed.split(",").indexOf(lang) !== -1;
if (visible) {
tplSelect.append(opt);
if ($(opt).val() === currentVal) currentStillValid = true;
}
});
// Options that were detached when the descriptions arrived
// missed that pass, so describe whatever is attached now.
// The cache is warm by this point, so this is synchronous.
applyTplDescriptions(tplSelect);
tplSelect.trigger("wiktku:refresh");
if (currentVal && !currentStillValid) {
tplSelect.val("");
tplTitleInput.hide().val("");
updateManeMode();
}
}
// Store updater on the element so refresh() can call it for all blocks.
tplSelect.data("updateOptions", updateTplOptions);
var tplTitleInput = $("<input type='text'>")
.addClass("wiktku-dfn-tpl-title wiktku-start-narrow")
.attr("placeholder", "Navê rûpela hedefê…")
.hide();
// Same reasoning as dfnArea above: refresh() (which rebuilds the
// Werger/Bi zaravayên din gloss list) is deferred to blur
// rather than run on every keystroke.
tplTitleInput.on("blur", function () { refresh(); });
// Şablon senses have no plain gloss, so hide alternatîv/hevmane/
// dijmane/etc. Etîket, Nimûne and Jêgirtin stay visible either
// way. The "Malûmatên din" accordion holds nothing but these
// groups, so it hides too instead of opening onto an empty box.
function updateFieldVisibility() {
var isTpl = !!tplSelect.val();
$.each(HIDDEN_ON_TPL_GROUPS, function (_, cls) {
block.find("." + cls + "-group").toggle(!isTpl);
});
advBox.toggle(!isTpl);
}
// Store on the block so restoreDfn() can re-sync visibility
// after setting tplSelect's value.
block.data("updateFieldVisibility", updateFieldVisibility);
tplSelect.on("change", function () {
var chosen = $(this).val();
if (chosen) {
dfnArea.hide();
nonKuWrap.hide();
tplTitleInput.show().focus();
} else {
tplTitleInput.hide().val("");
updateManeMode();
}
updateFieldVisibility();
// Re-run refresh so the Werger step visibility updates.
refresh();
});
// The template picker is an alternative to writing a
// definition, so this quiet link sits above the Mane box
// rather than getting buried under Dever/Etîket/Kategorî.
// It only expands for the minority of senses that are a
// pointer at another word (guharto, şaşnivîs, kevnbûyî)
// rather than a meaning.
var tplRow = $("<div></div>").addClass("wiktku-dfn-tpl-row")
.append(tplSelect).append(" ").append(tplTitleInput)
.hide();
var tplLink = $("<a href='#' class='wiktku-dfn-tpl-link'></a>")
.text("Maneyê bi şablonan çêke (rastnivîs, şasnivîs, awayekî din)");
tplLink.on("click", function (e) {
e.preventDefault();
tplRow.show();
$(this).hide();
tplRow.find("input.wiktku-ac-input").first().focus();
});
/** Opens the picker without a click — used on restore, when a
* sense already has a template and hiding it would make the
* chosen value invisible. */
function revealTplRow() { tplRow.show(); tplLink.hide(); }
block.data("revealTplRow", revealTplRow);
// — Dever/Şîve, Etîket, Kategorî: chip + toggle fields ("1c"
// design) — a small "+ …" toggle on the sense rather than an
// always-visible field, so senses that leave them empty (most
// of them) don't get a longer form. See getOutput() for how
// Dever/Şîve folds into {{dever|}} (ku) or {{e|}} (every other
// language) on the "#" line. —
// Only one of these three panels should be open at a time —
// each closes the other two on open (see buildChipField's
// onOpen/close). Safe to reference fields not yet assigned
// below: onOpen only ever runs later, from a click.
var deverField = buildChipField({
wrapCls: "wiktku-dfn-dever",
toggleLabel: "+ Dever/Şîve",
hint: "Bi vê maneyê li kû derê an di kîjan devokê de tê bikaranîn?",
suggestions: DEVER_SUGGESTIONS,
onOpen: function () { etiketField.close(); kategoriField.close(); }
});
block.data("addDeverChip", deverField.addChip);
var etiketField = buildChipField({
wrapCls: "wiktku-dfn-etiket",
toggleLabel: "+ Etîket",
hint: "xisûsen, Mêrdîn, anatomî, Badînî",
onOpen: function () { deverField.close(); kategoriField.close(); }
});
block.data("addEtiketChip", etiketField.addChip);
var kategoriField = buildChipField({
wrapCls: "wiktku-dfn-kategori",
toggleLabel: "+ Kategorî",
hint: "wekî etîket lê xuya nabe tenê kategorîze dike",
onOpen: function () { deverField.close(); etiketField.close(); }
});
block.data("addKategoriChip", kategoriField.addChip);
// Dever/Şîve starts open for ku (where it's the common case)
// and closed otherwise; backs off once the person toggles it
// themselves (see buildChipField's setOpenDefault). Re-synced
// from refresh() whenever lang changes — see block.data below.
function updateDeverOpenState() {
deverField.setOpenDefault((getValues().lang || "") === "ku");
}
block.data("updateDeverOpenState", updateDeverOpenState);
updateDeverOpenState();
// Toggles always sit together in one row; panels render in their
// own area below so opening one never displaces the others.
var chipFieldRow = $("<div class='wiktku-dfn-chipfield-row'></div>")
.append(deverField.wrap).append(etiketField.wrap).append(kategoriField.wrap);
var chipFieldPanels = $("<div class='wiktku-dfn-chipfield-panels'></div>")
.append(deverField.panel).append(etiketField.panel).append(kategoriField.panel);
block.append(tplLink);
block.append(tplRow);
block.append(dfnArea);
block.append(nonKuWrap);
block.append(chipFieldRow);
block.append(chipFieldPanels);
// Apply lang filter on initial build.
updateTplOptions();
updateManeMode();
// — Sub-field groups. —
// The four most-used groups (Nimûne, Jêgirtin, Hevmane, Dijmane)
// stay open; the rest sit behind the "Malûmatên din" toggle
// (adv: true → .wiktku-dfn-subgroup-adv, keyed off by the collapse
// CSS). Etîket/Kategorî used to be here too (adv: true) but are
// now their own chip+toggle fields (see deverField/etiketField/
// kategoriField above) — the same "1c" pattern already keeps
// them out of the way when empty, so they no longer need the
// advanced section either.
// Jêgirtin isn't in this array — it's a compound widget built
// after the loop — but it's inserted right after Nimûne so the
// visible order matches the list above.
var allGroups = [
{ cls: "wiktku-dfn-nimune", label: "Nimûne", rows: 2, narrow: false },
{ cls: "wiktku-dfn-hevmane", label: "Hevmane", rows: 1, narrow: true, izah: "Spehî hevmaneya Bedew e." },
{ cls: "wiktku-dfn-dijmane", label: "Dijmane", rows: 1, narrow: true, izah: "Zengîn dijmaneya feqîr e." },
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv", rows: 1, narrow: true, izah: "Varyantên vê kelîmeyê", adv: true },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne", rows: 1, narrow: true, izah: "Pisîk û kûçik termên koordîne ne çunkî Jornavên wan gelek in", adv: true },
{ cls: "wiktku-dfn-jornav", label: "Jornav", rows: 1, narrow: true, izah: "Heywan jornavê guhandar e", adv: true },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav", rows: 1, narrow: true, izah: "Guhandar jêrnavê heywan e", adv: true }
];
// adv:true groups land inside this box instead of directly in
// block — see the "Malûmatên din" accordion built after
// the loop, which appends advBox right where the old toggle
// used to sit (right after Dijmane, since that's the loop's
// last non-adv, block-appended group).
var advBox = $("<div class='wiktku-sense-adv-box'></div>");
$.each(allGroups, function (_, g) {
var groupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup " + g.cls + "-group");
if (g.adv) groupDiv.addClass("wiktku-dfn-subgroup-adv");
var groupParent = g.adv ? advBox : block;
// Declared once here rather than with `var` in each branch
// below — both branches assign the same pair, just with a
// different lbl and list, so one declaration avoids the
// (harmless but lint-flagged) re-declaration.
var lbl, list;
function buildNimuneLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-nimune-li");
var mainTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-text-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-text")
.attr("placeholder", "Nimûneya emilandina kelîmeyê tu bi xwe binivîse…");
mainTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(list.children("li:last-child"));
if (val && isLast) {
list.append(buildNimuneLi());
}
});
li.append(mainTa);
var subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
if (needsTransliteration(lang)) {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyona bi alfabeya latînî")
);
}
if (lang && lang !== "ku") {
subWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî")
);
}
if (subWrap.children().length) li.append(subWrap);
return li;
}
// The placeholder names the current language, but that's
// often not chosen yet when this block is first built —
// so keep a function around that refresh() (and onSubInput,
// for newly-grown fields) can call again once lang is set
// or changes.
function subPlaceholder() {
if (g.noLangSuffix) return g.label + "…";
var lang = getValues().lang || "";
var langLabel = lang ? getLangLabel(lang) : "";
return g.label + (langLabel ? " bi " + langLabel : "") + "…";
}
function updateSubPlaceholders() {
list.find("> li > textarea.wiktku-dfn-subfield").attr("placeholder", subPlaceholder());
}
if (g.cls === "wiktku-dfn-nimune") {
// Nimûne — compound widget with tr/t sub-fields.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ");
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
groupDiv.data("buildNimuneLi", buildNimuneLi);
list.append(buildNimuneLi());
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
} else {
// Regular always-visible group — label + growing textarea list.
lbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text(g.label))
.append(": ")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-hint").text(g.izah));
var useModifiers = !!MODIFIER_SUBGROUPS[g.cls];
list = $("<ol></ol>").addClass("wiktku-dfn-sublist " + g.cls + "-list");
list.data("updatePlaceholders", updateSubPlaceholders);
var firstInput = $("<textarea></textarea>")
.attr("name", g.cls + "-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", g.rows)
.attr("class", "wiktku-dfn-subfield" + (g.narrow ? " wiktku-start-narrow" : ""))
.attr("placeholder", subPlaceholder());
firstInput.on("input", function () {
steps.dfn.onSubInput(list, this, g.rows, g.narrow, useModifiers);
});
list.append(useModifiers ? makeModifierLi(firstInput) : $("<li></li>").append(firstInput));
groupDiv.append(lbl).append(list);
groupParent.append(groupDiv);
}
});
// — Jêder group — growing list, same structure as Nimûne. —
(function () {
var jederGroupDiv = $("<div></div>").addClass("wiktku-dfn-subgroup wiktku-dfn-jeder-group");
var jederLbl = $("<span></span>").addClass("wiktku-dfn-sublabel")
.append($("<span></span>").addClass("wiktku-dfn-sublabel-term").text("Jêgirtin"))
.append(": ");
var jederList = $("<ol></ol>").addClass("wiktku-dfn-sublist wiktku-dfn-jeder-list");
function buildJederLi() {
var lang = getValues().lang || "";
var li = $("<li></li>").addClass("wiktku-dfn-jeder-li");
// Quote text textarea
var quoteTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote")
.attr("placeholder", "Nimûneya emilandinê ji kitêb an çavkaniyeke din binivîse");
quoteTa.on("input", function () {
highlightLinebreak(this);
var val = $.trim($(this).val());
var isLast = li.is(jederList.children("li:last-child"));
if (val && isLast) {
jederList.append(buildJederLi());
}
});
li.append(quoteTa);
// tr/t sub-fields for the quote — shown as needed for the language.
var quoteSubWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
if (needsTransliteration(lang)) {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…")
);
}
if (lang && lang !== "ku") {
quoteSubWrap.append(
$("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî")
);
}
if (quoteSubWrap.children().length) li.append(quoteSubWrap);
// Citoid auto-fill: fetch citation data from a URL, DOI or ISBN and
// prefill the source şablon + fields below (see Gadget-citoid-helper.js).
var citoidWrap = $("<div></div>").addClass("wiktku-dfn-quote-citoid");
var citoidInput = $("<textarea></textarea>")
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-citoid-input")
.attr("rows", 1)
.attr("placeholder", "URL, DOI an ISBN binivîse û pêl bike bişkoka 'Bi otomatîk tijî bike' da ku otomatîk were tijîkirin…");
var citoidBtn = new OO.ui.ButtonWidget({
label: "Bi otomatîk tijî bike",
classes: [ "wiktku-dfn-quote-citoid-btn", "wiktku-add-sense-btn" ]
});
var citoidStatus = $("<span></span>").addClass("wiktku-dfn-quote-citoid-status");
// Fetches citation data from Citoid and fills this citation's
// şablon + fields — only rebuilds fields on template change,
// and never overwrites values the user already typed.
function runCitoidFetch() {
var query = $.trim(citoidInput.val());
if (!query) {
mw.notify("Ji kerema xwe re pêşî URL, DOI an ISBNyekê binivîse.");
return;
}
citoidBtn.setDisabled(true);
citoidStatus.text("Tê anîn…");
wikt.citoid.fetchJederFields(query).then(function (result) {
var jederKey = result.jederKey;
var hasTpl = !!jederKey && srcSel.find("option[value='" + jederKey + "']").length > 0;
var targetVal = hasTpl ? jederKey : "__manual__";
// Only rebuild if switching şablon, so a repeat fetch
// doesn't clear fields already filled in by hand.
if (srcSel.val() !== targetVal) {
srcSel.val(targetVal).trigger("change");
}
if (hasTpl) {
// Tenê qadên vala tijî bike.
$.each(result.fields, function (key, val) {
var input = srcFields.find("[data-field-key='" + key + "']");
if (input.length && !$.trim(input.val())) {
input.val(val);
}
});
} else {
// Şablonek nehat naskirin ji bo vê itemType — rêzikeke
// çavkaniyê ya bi destan a ji qadên Citoid tê çêkirin,
// lê tenê ger qada destan vala be.
var manualField = srcFields.find(".wiktku-dfn-quote-src-manual");
if (!$.trim(manualField.val())) {
var parts = [];
$.each(["nivîskar", "sernav", "websîte", "kovar", "weşanxane", "sal", "url"], function (_, k) {
if (result.fields[k]) parts.push(result.fields[k]);
});
manualField.val(parts.join(", "));
}
}
citoidStatus.text("");
}, function (errorMsg) {
citoidStatus.text(typeof errorMsg === "string" ? errorMsg : "Nehat dîtin. Kontrol bike ka URL/DOI/ISBN rast e.");
}).always(function () {
citoidBtn.setDisabled(false);
});
}
citoidBtn.on("click", runCitoidFetch);
citoidInput.on("keydown", function (e) {
if (e.which === 13) { // Enter
e.preventDefault();
runCitoidFetch();
}
});
citoidWrap.append(citoidInput).append(citoidBtn.$element).append(citoidStatus);
// Source template selector, specific to this citation.
var srcSel = $("<select></select>").addClass("wiktku-dfn-quote-src-sel wiktku-plain-select");
srcSel.append($("<option value=''>— Çavkaniyê hilbijêre —</option>"));
srcSel.append($("<option></option>").val("__manual__").text("Bi destê xwe binivîsim"));
srcSel.append($("<option></option>").val("__category__").text("Ji Kategorî:Çavkanî şablonekê hilbijêre"));
var jederDefs = steps.dfn.jederDefs || {};
$.each(jederDefs, function (tplName, def) {
srcSel.append($("<option></option>").val(tplName).text(def.label));
});
// Dynamic source fields container.
var srcFields = $("<div></div>").addClass("wiktku-dfn-quote-src-fields");
srcSel.on("change", function () {
srcFields.empty();
var chosen = $(this).val();
// Citoid is only useful before a source is chosen —
// hide it once a selection is made, restore on reset.
if (chosen) {
citoidWrap.hide();
} else {
citoidWrap.show();
return;
}
if (chosen === "__manual__") {
// Freeform source, written by the user — becomes the 2nd
// positional param of {{jêder|lang|…}} instead of çavkanî?.
srcFields.append(
$("<textarea></textarea>")
.attr("rows", 4)
.addClass("wiktku-dfn-subfield wiktku-dfn-quote-src-manual")
.attr("placeholder", "Çavkaniyê bi destê xwe binivîse…")
);
return;
}
if (chosen === "__category__") {
// Search widget over the members of [[Kategorî:Çavkanî]].
// Picking a şablon here immediately switches the row to
// "Bi destê xwe binivîsim" mode with {{Şablon}} pre-filled,
var catWrap = $("<span></span>").addClass("wiktku-native-select-wrap wiktku-dfn-quote-src-cat");
var catInput = $("<input type='search' autocomplete='off'>")
.addClass("wiktku-start-narrow wiktku-dfn-quote-src-cat-input")
.attr("placeholder", "Li navê şablonê bigere…");
var catResults = $("<ul></ul>").addClass("wiktku-ac-results wiktku-dfn-quote-src-cat-results").hide();
var catStatus = $("<span></span>").addClass("wiktku-dfn-quote-src-cat-status").text("Şablon tên barkirin…");
var showCatResults = function (query) {
var q = $.trim(query).toLowerCase();
catResults.empty();
var list = catSrcTemplates || [];
var matched = q ?
$.grep(list, function (name) { return name.toLowerCase().indexOf(q) !== -1; }) :
list;
if (!matched.length) {
catResults.hide();
return;
}
$.each(matched.slice(0, 50), function (_, name) {
var li = $("<li></li>").text(name).attr("data-value", name);
var touchMoved = false;
li.on("touchstart", function () { touchMoved = false; });
li.on("touchmove", function () { touchMoved = true; });
li.on("touchend", function (e) {
if (touchMoved) return;
e.preventDefault();
selectCatTpl(name);
});
li.on("mousedown", function (e) {
e.preventDefault();
selectCatTpl(name);
});
catResults.append(li);
});
catResults.show();
};
// Wraps the chosen şablon as {{Name}}, drops it into the
// manual textarea, and switches the row into manual mode
// so the user can keep editing it (add params, etc.).
var selectCatTpl = function (name) {
catResults.hide();
srcSel.val("__manual__");
srcSel.trigger("change");
srcFields.find(".wiktku-dfn-quote-src-manual")
.val("{{" + name + "}}")
.trigger("focus");
};
catInput.on("input", function () {
showCatResults($(this).val());
});
catInput.on("focus", function () {
showCatResults($(this).val());
});
catInput.on("blur", function () {
setTimeout(function () { catResults.hide(); }, 200);
});
catWrap.append(catInput).append(catResults).append(catStatus);
srcFields.append(catWrap);
fetchCatSrcTemplates().then(function () {
catStatus.text("");
}, function () {
catStatus.text("Şablon nehatin barkirin.");
});
return;
}
var def = (steps.dfn.jederDefs || {})[chosen];
if (!def) return;
// Wide/narrow/medium layout, same field shape as
// MediaWiki:Gadget-wikt.add-examples.js: sernav-type
// fields take a full-width multiline box, short ones
// like sal/rûpel/isbn stay narrow, everything else is
// a plain single-line medium field.
$.each(def.fields, function (_, field) {
var row = $("<div></div>")
.addClass("wiktku-dfn-quote-src-row wiktku-dfn-quote-src-row--" + (field.width || "medium"));
var fieldLbl = $("<label></label>").text(field.label + ": ");
var inp = $(field.multiline ? "<textarea></textarea>" : "<input type='text'>")
.addClass("wiktku-dfn-quote-src-input")
.attr("data-field-key", field.key)
.attr("placeholder", field.label + "…");
if (field.multiline) {
inp.attr("rows", 2).addClass("wiktku-dfn-quote-src-input--title");
}
row.append(fieldLbl).append(inp);
srcFields.append(row);
});
});
li.append(citoidWrap).append(srcSel).append(srcFields);
return li;
}
jederGroupDiv.data("buildJederLi", buildJederLi);
jederList.append(buildJederLi());
jederGroupDiv.append(jederLbl).append(jederList);
// Straight after Nimûne rather than last: Jêgirtin is the
// same kind of thing (an attested use of the word) and the
// two belong together — see the allGroups comment above.
var nimuneGroup = block.find(".wiktku-dfn-nimune-group");
if (nimuneGroup.length) jederGroupDiv.insertAfter(nimuneGroup);
else block.append(jederGroupDiv);
}());
// Apply the initial hide/show state for the sub-groups (matches
// tplSelect's starting value, which is normally empty).
updateFieldVisibility();
// ── "Malûmatên din" — accordion, per sense, after
// Dijmane. Fieldset-style: advBox (built before the allGroups
// loop above, which appends every adv:true group's div into
// it instead of straight into block) gets a legend that
// floats on its top border — diamond + label + "?" help +
// chevron — plus a one-line summary shown only while
// collapsed, so a glance says whether anything's back there
// without opening it. One toggle per sense rather than one
// for the whole step: the fields behind it (Formên
// alternatîv, Jornav…) belong to a single meaning, so a
// global switch flipped them for every sense at once and gave
// a count that answered "across all senses" — not a question
// anybody was asking.
(function () {
block.addClass("wiktku-sense-adv-hidden");
advBox.addClass("wiktku-sense-adv-box");
var advFields = [
{ cls: "wiktku-dfn-alternativ", label: "Formên alternatîv" },
{ cls: "wiktku-dfn-koordine", label: "Termên koordîne" },
{ cls: "wiktku-dfn-jornav", label: "Jornav" },
{ cls: "wiktku-dfn-jernav", label: "Jêrnav" }
];
var legend = $("<div class='wiktku-sense-adv-legend'></div>");
legend.append($("<span class='wiktku-sense-adv-diamond'>◆</span>"));
legend.append($("<span class='wiktku-sense-adv-label'>Malûmatên din</span>"));
var chevron = $("<button type='button' class='wiktku-sense-adv-chevron' title='Veke/bigire'></button>")
.attr("aria-expanded", "false");
legend.append(chevron);
var summary = $("<div class='wiktku-sense-adv-summary'></div>");
var summaryText = $("<span class='wiktku-sense-adv-summary-text'></span>");
summary.append($("<span class='wiktku-sense-adv-summary-dots'>⋯</span>"))
.append(summaryText);
function syncAdv() {
var hidden = block.hasClass("wiktku-sense-adv-hidden");
chevron.text(hidden ? "▾" : "▲").attr("aria-expanded", hidden ? "false" : "true");
advBox.toggleClass("wiktku-sense-adv-open", !hidden);
var filledLabels = [];
$.each(advFields, function (_, f) {
var any = block.find("." + f.cls + "-list textarea").filter(function () {
return $.trim($(this).val()) !== "";
}).length > 0;
if (any) filledLabels.push(f.label);
});
summaryText.text(filledLabels.length ? filledLabels.join(", ") : "Tiştek nehatiye dagirtin");
}
chevron.on("click", function (e) {
e.preventDefault();
block.toggleClass("wiktku-sense-adv-hidden");
syncAdv();
});
block.data("syncSenseAdv", syncAdv);
advBox.prepend(summary).prepend(legend);
// After the last always-visible group (Dijmane) and before
// the advanced ones (now inside advBox), which sit below
// it in DOM order.
var lastPrimary = block.find(".wiktku-dfn-dijmane-group");
if (lastPrimary.length) advBox.insertAfter(lastPrimary);
else block.append(advBox);
syncAdv();
}());
return block;
},
/** Shows only `block` among the sense list; hides every other one. */
setOpenSense: function (stepDiv, block) {
stepDiv.find("li.wiktku-dfn-sense").each(function () {
$(this).toggleClass("wiktku-sense-collapsed", !$(this).is(block));
});
steps.dfn.renderSenseTabs(stepDiv);
},
/** Rebuilds the "Mane 1 / Mane 2 / [trash] / + Maneyeke din" tab row from the current sense list. */
renderSenseTabs: function (stepDiv) {
var tabsDiv = stepDiv.find(".wiktku-dfn-sense-tabs");
if (!tabsDiv.length) return;
tabsDiv.empty();
var blocks = stepDiv.find("li.wiktku-dfn-sense");
var openLi = null;
blocks.each(function (i) {
var li = $(this);
var isOpen = !li.hasClass("wiktku-sense-collapsed");
if (isOpen) openLi = li;
var tab = $("<span class='wiktku-dfn-sense-tab'></span>")
.text("Mane " + (i + 1))
.toggleClass("wiktku-tab-active", isOpen);
tab.on("click", function () { steps.dfn.setOpenSense(stepDiv, li); });
tabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — so
// there's only ever a single, deliberate hit-target for a
// destructive action, positioned away from the tabs people
// click to switch senses. Always keep at least one Mane.
if (blocks.length > 1 && openLi) {
var openIdx = blocks.index(openLi);
var trash = makeTrashButton("Vê maneyê jê bibe (Mane " + (openIdx + 1) + ")");
trash.on("click", function () {
if (!window.confirm("Tu dixwazî Mane " + (openIdx + 1) + " jê bibî? Ev tişt nayê vegerandin.")) return;
steps.dfn.removeSense(stepDiv, openLi);
});
tabsDiv.append(trash.$element);
}
var addBtn = $("<button type='button' class='wiktku-add-sense'></button>")
.append($("<span class='wiktku-add-sense-plus'>+</span>"))
.append(document.createTextNode("Maneyeke din"));
addBtn.on("click", function (e) {
e.preventDefault();
steps.dfn.addSense(stepDiv);
});
tabsDiv.append(addBtn);
},
/** Appends a sense, opens it, and hides the others. */
addSense: function (stepDiv) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var newBlock = steps.dfn.buildSenseBlock(stepDiv);
list.append(newBlock);
initSelects(newBlock);
steps.dfn.setOpenSense(stepDiv, newBlock);
newBlock.find("textarea.wiktku-dfn-mane").first().focus();
refresh();
return newBlock;
},
/** Removes one sense block. Assumes the caller already confirmed
* and already checked more than one sense remains. If the
* removed sense was the open one, opens whichever sense ends up
* taking its place (or the last one, if it was the last). */
removeSense: function (stepDiv, li) {
var list = stepDiv.find("ol.wiktku-dfn-list");
var blocks = list.find("li.wiktku-dfn-sense");
var removedIdx = blocks.index(li);
var wasOpen = !li.hasClass("wiktku-sense-collapsed");
li.remove();
if (wasOpen) {
var remaining = list.find("li.wiktku-dfn-sense");
var next = remaining.eq(removedIdx);
if (!next.length) next = remaining.last();
if (next.length) steps.dfn.setOpenSense(stepDiv, next);
}
steps.dfn.renderSenseTabs(stepDiv);
refresh();
},
/** Grows a sub-list (nimûne/formên alternatîv/hevmane/dijmane/termên koordîne) on input. */
onSubInput: function (list, input, rows, narrow, modifiers) {
var val = $.trim($(input).val());
var item = $(input).parent();
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
var next = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-sub-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", rows)
.attr("class", "wiktku-dfn-subfield" + (narrow ? " wiktku-start-narrow" : ""));
next.on("input", function () {
steps.dfn.onSubInput(list, this, rows, narrow, modifiers);
});
list.append(modifiers ? makeModifierLi(next) : $("<li></li>").append(next));
// New field starts with no placeholder — give it (and any
// others in the list) the current one.
var updatePlaceholders = list.data("updatePlaceholders");
if (typeof updatePlaceholders === "function") updatePlaceholders();
}
},
// Populated from [[MediaWiki:Gadget-start.js/data.json]] (tplDefs, jederDefs).
tplDefs: {},
jederDefs: {},
build: function (stepDiv) {
if (stepDiv.find("ol.wiktku-dfn-list").length) return null;
var tabsDiv = $("<div class='wiktku-dfn-sense-tabs'></div>");
stepDiv.append(tabsDiv);
var list = $("<ol class='wiktku-dfn-list'></ol>");
list.append(steps.dfn.buildSenseBlock(stepDiv));
stepDiv.append(list);
steps.dfn.renderSenseTabs(stepDiv);
return null;
},
/** Reads all sense blocks from the DOM and returns an array of sense objects. */
getSenses: function (stepDiv) {
var senses = [];
stepDiv.find("li.wiktku-dfn-sense").each(function () {
var block = $(this);
var tplSel = block.find("select.wiktku-dfn-tpl-select");
var tpl = tplSel.val() || "";
// noLang: template already encodes the language — don't add |lang| param.
var tplNoLang = tpl
? tplSel.find("option[value='" + tpl + "']").attr("data-nolang") === "1"
: false;
var tplTitle = $.trim(block.find("input.wiktku-dfn-tpl-title").val());
var mane = $.trim(block.find("textarea.wiktku-dfn-mane").val());
function readList(cls) {
var vals = [];
block.find("." + cls + "-list textarea").each(function () {
var v = $.trim($(this).val());
if (v) vals.push(v);
});
return vals;
}
// Nimûne items are compound: { text, tr?, t? }
function readNimuneList() {
var items = [];
block.find(".wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var text = $.trim($(this).find(".wiktku-dfn-nimune-text").val());
if (!text) return;
var obj = { text: text };
var tr = $.trim($(this).find(".wiktku-dfn-nimune-tr").val());
var t = $.trim($(this).find(".wiktku-dfn-nimune-t").val());
if (tr) obj.tr = tr;
if (t) obj.t = t;
items.push(obj);
});
return items;
}
// Jêder is a growing list of citations, one per li — mirrors readNimuneList().
function readJederList() {
var items = [];
block.find(".wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
var quote = $.trim(li.find(".wiktku-dfn-nimune-quote").val());
if (!quote) return;
var obj = { quote: quote };
var qtr = $.trim(li.find(".wiktku-dfn-nimune-quote-tr").val());
var qt = $.trim(li.find(".wiktku-dfn-nimune-quote-t").val());
if (qtr) obj.quoteTr = qtr;
if (qt) obj.quoteT = qt;
var tplName = li.find(".wiktku-dfn-quote-src-sel").val();
if (tplName === "__manual__") {
var manual = $.trim(li.find(".wiktku-dfn-quote-src-manual").val());
if (manual) obj.jederManual = manual;
} else if (tplName === "__category__") {
// Reachable only if the user opened the category search but
// never picked a şablon — picking one switches the row to
// __manual__ (see selectCatTpl above), so there's nothing
// meaningful to save here.
} else if (tplName) {
obj.jederTpl = tplName;
var fields = {};
li.find(".wiktku-dfn-quote-src-input").each(function () {
var k = $(this).attr("data-field-key");
var v = $.trim($(this).val());
if (k && v) fields[k] = v;
});
obj.jêderFields = fields;
}
items.push(obj);
});
return items;
}
// Dever/Şîve, Etîket and Kategorî are chip fields (buildChipField),
// not textareas, so readList() doesn't reach them — each chip's
// value lives in data-value, scoped by the field's wrap class.
function readChipList(wrapCls) {
var vals = [];
block.find("." + wrapCls + "-wrap .wiktku-dfn-chipfield-chip").each(function () {
var v = $.trim($(this).attr("data-value") || "");
if (v) vals.push(v);
});
return vals;
}
senses.push({
tpl: tpl,
tplNoLang: tplNoLang,
tplTitle: tplTitle,
mane: mane,
etiket: readChipList("wiktku-dfn-etiket"),
kategori: readChipList("wiktku-dfn-kategori"),
nimune: readNimuneList(),
jeder: readJederList(),
alternativ: readList("wiktku-dfn-alternativ"),
hevmane: readList("wiktku-dfn-hevmane"),
dijmane: readList("wiktku-dfn-dijmane"),
koordine: readList("wiktku-dfn-koordine"),
jornav: readList("wiktku-dfn-jornav"),
jernav: readList("wiktku-dfn-jernav"),
dever: readChipList("wiktku-dfn-dever")
});
});
return senses;
},
getOutput: function (values, callback) {
var stepDiv = $("#wiktku-start-dfn");
var senses = steps.dfn.getSenses(stepDiv);
var lang = values.lang;
// A sense counts as "filled" if it has a mane, a chosen template + title,
// or any other content (etîket/hevmane/nimûne/jêder…) worth keeping.
var hasMane = senses.some(function (s) {
return s.mane !== "" || (s.tpl && s.tplTitle) || senseHasExtraContent(s);
});
var defLines = []; // one entry per kept sense (goes to linkify)
var citeLines = []; // parallel — nimûne+jêder for that same sense
var izahLines = []; // parallel — raw îzah gloss for that same sense
// True only once some sense's *actual* dfn text (post tercime/îzah
// split) is non-empty.
var hasLinkableMane = false;
$.each(senses, function (_, sense) {
// Declared once here rather than with `var` in each branch
// below — the template-sense branch returns early, so only
// one of the two assignments ever actually runs, but both
// are lexically in this same function scope.
var idx, cite;
// — Template sense (e.g. {{guharto|ku|somepage}}) —
if (sense.tpl) {
if (!sense.tplTitle) return; // skip empty template senses
var tplKatTpl = buildSenseTpl(sense, "kategori", lang, "K");
var tplDeverTpl = buildDeverTpl(sense, lang);
var tplLabelTpl = buildEtiketTpl(sense, lang);
var tplCall = sense.tplNoLang
? "{{" + sense.tpl + "|" + sense.tplTitle + "}}"
: "{{" + sense.tpl + "|" + lang + "|" + sense.tplTitle + "}}";
idx = defLines.length;
cite = buildCite(sense, lang);
defLines.push("# " + (tplKatTpl ? tplKatTpl + " " : "") + (tplDeverTpl ? tplDeverTpl + " " : "") +
(tplLabelTpl ? tplLabelTpl + " " : "") + tplCall +
(cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(""); // template senses have no îzah
return;
}
// — Normal mane sense —
var dfn = sense.mane;
if (!dfn && !senseHasExtraContent(sense)) return; // truly empty
// Pull the îzah gloss (if any) out of the combined tercime+îzah
// string — it's spliced back in separately below, after its
// own linkify pass (see izahLines / the second linkifyText call).
var izahRaw = null;
var tercimeIzah = splitTercimeIzah(dfn);
if (tercimeIzah) {
dfn = tercimeIzah.tercime;
izahRaw = stripWikilinks(tercimeIzah.izah);
}
if (dfn) {
dfn = stripWikilinks(dfn);
// Sentence-case + trailing period are for ku definitions,
// which are written as full glosses.
if (lang === "ku") {
if (dfn[0] !== dfn[0].toUpperCase()) dfn = capitalize(dfn);
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
}
// Real prose — this is what makes the sense worth running
// through the main linkify pass.
hasLinkableMane = true;
} else if (izahRaw === null) {
// No mane at all — this is just the "{{mane?|lang}}"
// placeholder, a bare template call with no plain words
// in it. Do NOT count it as linkable: sending nothing but
// bracketed templates through the main linkify pass hangs
// the linkify gadget (see the hasLinkableMane/hasIzah
// check below).
dfn = lang === "ku" ? "{{mane?|" + lang + "}}" : "";
}
var katTpl = buildSenseTpl(sense, "kategori", lang, "K");
var deverTpl = buildDeverTpl(sense, lang);
var labelTpl = buildEtiketTpl(sense, lang);
var out = "# " + (katTpl ? katTpl + " " : "") + (deverTpl ? deverTpl + " " : "") + (labelTpl ? labelTpl + " " : "") + dfn;
idx = defLines.length;
var hasThisIzah = izahRaw !== null && $.trim(izahRaw) !== "";
// Bare {{...}} token — like {{wiktku-cite-N}}, this survives
// linkify's first pass untouched (it treats template calls as
// opaque), then gets swapped for {{îzah|…}} once the actual
// gloss text has been linkified in its own separate pass.
if (hasThisIzah) {
out += (dfn ? " " : "") + "{{wiktku-izah-" + idx + "}}";
}
function wordLine(tpl, words) {
if (!words.length) return "";
return "\n#: {{" + tpl + "|" + lang + "|" + words.join("|") + "}}";
}
out += wordLine("alt-r", sense.alternativ);
out += wordLine("hevmane", sense.hevmane);
out += wordLine("dijmane", sense.dijmane);
out += wordLine("koordîne", sense.koordine);
out += wordLine("jornav", sense.jornav);
out += wordLine("jêrnav", sense.jernav);
cite = buildCite(sense, lang);
defLines.push(out + (cite ? "{{wiktku-cite-" + idx + "}}" : ""));
citeLines.push(cite);
izahLines.push(hasThisIzah ? izahRaw : "");
});
// Splice citeLines back into a linkified string, replacing each
// {{wiktku-cite-N}} token with its stored nimûne/jêder block.
function restoreCites(t) {
return t.replace(/\{\{wiktku-cite-(\d+)\}\}/g, function (_, n) {
return citeLines[parseInt(n, 10)] || "";
});
}
// Runs text through the linkify gadget if it's loaded, otherwise
// just passes it through unchanged. Shared by both linkify passes
// below (main mane text, and — separately — îzah glosses).
function linkifyText(t, cb) {
if (typeof $.kuLinkify === "function") {
$.kuLinkify(t, cb);
} else {
cb(t);
}
}
var hasIzah = izahLines.some(function (t) { return t && $.trim(t) !== ""; });
var text = defLines.join("\n") + "\n";
if (!hasMane) {
// No definitions entered — emit {{mane?}} with nimûne?.
text = "# {{mane?|" + lang + "}}\n#* {{nimûne?|" + lang + "}}\n";
callback(text);
return;
}
// Runs the îzah-only linkify pass against `base` (either the raw
// text, when there's no linkable mane to run the main pass on
// first, or the already-linkified main text). Shared by both
// branches below so the token-splice logic only lives once.
function runIzahPass(base) {
var izahJoined = $.map(izahLines, function (raw, idx) {
if (!raw) return undefined;
return "{{wiktku-izah-open-" + idx + "}}" + raw + "{{wiktku-izah-close-" + idx + "}}";
}).join("\n");
linkifyText(izahJoined, function (izahLinked) {
var izahByIdx = {};
var re = /\{\{wiktku-izah-open-(\d+)\}\}([\s\S]*?)\{\{wiktku-izah-close-\1\}\}/g;
var m;
while ((m = re.exec(izahLinked)) !== null) {
izahByIdx[parseInt(m[1], 10)] = m[2];
}
var withIzah = base.replace(/\{\{wiktku-izah-(\d+)\}\}/g, function (_, n) {
var linked = izahByIdx[parseInt(n, 10)];
return linked !== undefined ? "{{îzah|" + linked + "}}" : "";
});
callback(restoreCites(withIzah));
});
}
if (!hasLinkableMane) {
if (!hasIzah) {
// All filled senses are template senses (or otherwise have
// no plain text at all) — nothing here needs linkify.
callback(restoreCites(text));
return;
}
// There's no plain mane text anywhere (e.g. tercime left blank
// with only îzah filled in), so every defLine is just a bare
// template call with nothing to word-link. Sending a line
// like "# {{wiktku-izah-0}}" through the main linkify pass
// gives it no plain words to work with, makes the linkify
// gadget hang — so skip that pass entirely and run only
// the îzah pass, splicing straight into the unlinkified text.
var loadIzahOnly = function () { runIzahPass(text); };
mw.loader.using("ext.gadget.linkify", loadIzahOnly, loadIzahOnly);
return;
}
var didTryToLoad = function () {
// First pass: the main definition text (mane), same as before.
linkifyText(text, function (mainLinked) {
if (!hasIzah) {
callback(restoreCites(mainLinked));
return;
}
// Second pass: îzah glosses, linkified via their own
// separate call. Each gloss is wrapped in its own
// {{wiktku-izah-open/close-N}} pair; like the existing
// {{wiktku-cite-N}} tokens, these sit inside double
// braces so linkify leaves the tokens themselves alone —
// only the raw gloss text between each pair gets linked.
runIzahPass(mainLinked);
});
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
}
},
tewandin: {
id: "tewandin",
name: "tewandin",
question_fmt: 'Tewandina "' + pageName + '" binivîse',
visible: function (values) {
return values.lang && values.pos;
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-tewandin-wrap").length) return null;
var wrap = $("<div class='wiktku-tewandin-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-tewandin-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-tewandin-text")
.attr("rows", 4);
wrap.append(ta);
stepDiv.append(wrap);
function autoPopulate() {
if (!ta.length) return;
var vals = getValues();
var c = vals.cinsiyet || "";
var p = vals.pos || "";
if (!pageName.includes(" ") && c && GENDER_BASE_LABELS[c] &&
(p === "navdêr" || p === "serenav") && vals.lang === "ku" &&
c !== "mndudil") {
var label = (c === "mnmane") ? "mê û nêr" : GENDER_BASE_LABELS[c];
var out = "{{ku-tewîn-nav|" + label;
if (p === "serenav") out += "|y";
out += "}}";
if (!$.trim(ta.val())) ta.val(out);
}
}
// The example placeholder only fits Kurmancî nouns — showing it
// for a verb or another language would suggest the wrong template.
function updatePlaceholder() {
if (!ta.length) return;
var vals = getValues();
ta.attr("placeholder", (vals.lang === "ku" && vals.pos === "navdêr") ? "{{ku-tewîn-nav|nêr}}" : "");
}
setTimeout(function () {
autoPopulate();
updatePlaceholder();
$("#wiktku-start-form").on("change.tewandin",
"#wiktku-start-lang-field, #wiktku-start-cinsiyet-field, #wiktku-start-pos-field",
function () { autoPopulate(); updatePlaceholder(); });
}, 0);
return ta;
},
output: function () {
var manualText = $.trim($("#wiktku-start-tewandin .wiktku-tewandin-text").val());
if (manualText) {
return "\n==== Tewandin ====\n" + manualText + "\n";
}
return "";
}
},
// resim — one or more images
resim: {
id: "resim",
name: "resim",
question_fmt: "Resmekê lê zêde bike",
visible: function (values) { return !!(values.lang && values.pos); },
/** Builds one image row: filename + caption + alt text. */
buildItem: function (stepDiv) {
var item = $("<li class='wiktku-resim-item'></li>");
var fileRow = $("<div class='wiktku-posparams-row'></div>");
var fileInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-file")
.attr("placeholder", "navê_dosyayê.jpg");
fileRow.append($("<label></label>").text("Navê dosyayê: ")).append(fileInp);
var capRow = $("<div class='wiktku-posparams-row'></div>");
var capInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-caption")
.attr("placeholder", "Danasîna resimê…");
capRow.append($("<label></label>").text("Danasîn: ")).append(capInp);
// Alt text: what a screen reader announces for this image
var altRow = $("<div class='wiktku-posparams-row'></div>");
var altInp = $("<input type='text'>")
.addClass("wiktku-start-step-field wiktku-start-narrow wiktku-resim-alt")
.attr("placeholder", "Ji bo kesên nabînin, çi tê dîtin? (ne mecbûrî)");
altRow.append($("<label></label>").text("Nivîsa alternatîf: ")).append(altInp);
// Auto-fill the caption with the plain page name when a filename
// is typed
fileInp.on("input", function () {
var file = $.trim($(this).val());
var curCap = $.trim(capInp.val());
var isDefault = !curCap || curCap === pageName;
if (file && isDefault) {
capInp.val(pageName);
} else if (!file && isDefault) {
capInp.val("");
}
steps.resim.onFileInput(stepDiv, item, this);
});
item.append(fileRow).append(capRow).append(altRow);
return item;
},
/** Grows the image list when the last row's filename gets content. */
onFileInput: function (stepDiv, item, input) {
var val = $.trim($(input).val());
var list = stepDiv.find("ol.wiktku-resim-list");
var isLast = item.is(list.children("li:last-child"));
if (val && isLast) {
list.append(steps.resim.buildItem(stepDiv));
}
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-resim-wrap").length) return null;
var wrap = $("<div class='wiktku-resim-wrap'></div>");
var list = $("<ol class='wiktku-resim-list'></ol>");
list.append(steps.resim.buildItem(stepDiv));
wrap.append(list);
stepDiv.append(wrap);
return null;
},
/** Reads every filled row from the DOM into an array of {file, caption, alt}
* objects — blank rows (missing filename or caption) are skipped.
* Non-Kurdish captions get wrapped in {{zim|lang|…}} here, unless
* the user already typed their own {{zim|…}} template. Alt text is
* optional and left as plain text either way. */
getImages: function (values) {
var lang = (values && values.lang) || getValues().lang || "";
var images = [];
$("#wiktku-start-resim li.wiktku-resim-item").each(function () {
var file = $.trim($(this).find(".wiktku-resim-file").val());
var caption = $.trim($(this).find(".wiktku-resim-caption").val());
var alt = $.trim($(this).find(".wiktku-resim-alt").val());
if (!file || !caption) return;
// Strip leading "Wêne:" or "File:" if the user typed it — we add it ourselves.
file = file.replace(/^(wêne|file|image)\s*:\s*/i, "");
if (lang && lang !== "ku" && !/^\{\{zim\s*\|/i.test(caption)) {
caption = "{{zim|" + lang + "|" + caption + "}}";
}
images.push({ file: file, caption: caption, alt: alt });
});
return images;
},
output: function (values) {
var images = steps.resim.getImages(values);
if (!images.length) return "";
return images.map(function (img) {
var altPart = img.alt ? "|alt=" + img.alt : "";
return "[[Wêne:" + img.file + "|thumb" + altPart + "|" + img.caption + "]]";
}).join("\n") + "\n";
}
},
// h-bnr — {{h-bnr|Page1|Page2|…}} prepended before the lang section (shared, not per-section)
bnrh: {
id: "bnrh",
name: "herwiha binêre (h-bnr)",
question_fmt: "Şablona h-bnr li qismê herî jor a rûpelê",
//visible: function (values) { return !!values.lang; },
multiple: true,
narrow: true,
placeholder: "Ji bo 'test' bnr 'Test', 'teşt'",
output: function (values) {
var pages = $.grep(values.bnrh || [], function (p) { return !!$.trim(p); });
if (!pages.length) return "";
return "{{h-bnr|" + pages.map(function (p) { return $.trim(p); }).join("|") + "}}\n";
}
},
bikaranin: {
id: "bikaranin",
name: "Notên bikaranînê",
question_fmt: "Notên bikaranînê yên li ser vê kelîmeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
output: function (values) {
var text = $.trim(values.bikaranin || "");
if (!text) return "";
return "\n==== Notên bikaranînê ====\n" + text + "\n";
},
},
xwendin: {
id: "xwendin",
name: "Bêhtir bixwîne",
question_fmt: "Lînkên derveyî eleqedarî vê madeyê",
visible: function (values) {
return values.lang && values.pos;
},
multiple: false,
narrow: false,
placeholder: function (values) {
if (!values || !values.lang) {
return `* {{wpedî}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
}
const ziman = values.lang === "ku" ? "" : `|ziman=${values.lang}`;
return `* {{wpedî${ziman}}} ji bo ku meqaleya Wîkîpediyayê lînk bidî`;
},
output: function (values) {
var text = $.trim(values.xwendin || "");
if (!text) return "";
return "\n==== Bêhtir bixwîne ====\n" + text + "\n";
},
},
alfabeyendin: {
id: "alfabeyendin",
name: "Bi alfabeyên din",
question_fmt: function (values) {
var base = "Nivîsîna vê kelîmeyê bi alfabeyên din"
var langLabel = values.lang ? getLangLabel(values.lang) : "";
if (langLabel) {
return base + " ên " + langLabel;
}
return base;
},
// Latin-script Kurdish itself, and languages with only one script
// on record, don't need this — but any language metadata.json
// lists more than one script for (data.json's "sc") clearly does.
visible: function (values) {
if (!(values.lang && values.pos)) return false;
var lang = values.lang;
return lang === "ku" || ZARAVA_LANG_CODES.indexOf(lang) !== -1 || langHasMultipleScripts(lang);
},
multiple: false,
narrow: false,
placeholder: "Ji bo kurmancî: * {{ku-ar|şiklê alfaba erebî}}",
output: function (values) {
var text = $.trim(values.alfabeyendin || "");
if (!text) return "";
return "\n==== Bi alfabeyên din ====\n" + text + "\n";
},
},
formalt: makeWordListStep("formalt", "formên alternatîv", "Formên alternatîv ên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
hevmane: makeWordListStep("hevmane", "hevmane", "Hevmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
dijmane: makeWordListStep("dijmane", "dijmane", "Dijmaneyên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jornav: makeWordListStep("jornav", "jornav", "Jornavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
jernav: makeWordListStep("jernav", "jêrnav", "Jêrnavên vê kelîmeyê ji bo hemû maneyan eger te li binê maneyê nenivîsandibe"),
je: makeWordListStep("je", "jê", "Kelîmeyên jê çêbûyî binivîse"),
tekildar: makeWordListStep("tekildar", "têkildar", "Kelîmeyên ji aliyê etîmolojiyê ve têkildarî vê kelîmeyê binivîse"),
binereherwiha: makeWordListStep("binereherwiha", "herwiha binêre", "Kelîmeyên din ên eleqedar binivîse"),
etym: {
id: "etym",
name: "etîmolojî",
question_fmt: "Etîmolojiya kelîmeyê binivîse",
// etymTpls populated from data.json
etymTpls: {},
visible: function (values) {
return values.lang && values.pos;
},
// Fills the insert-dropdown from the current etymTpls object.
refreshTplSelect: function () {
var sel = $("#wiktku-start-etym .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
markTemplateOptions(sel, function (opt) { return tplNameFromSnippet(opt.val()); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-etym-wrap").length) return null;
var wrap = $("<div class='wiktku-etym-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-etym-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-etym-text")
.attr("rows", 4)
.attr("placeholder", "Ji {{ji|en|ku|apple}}. yan rasterast binivîse…");
// Template insert dropdown — options also refreshed after data.json loads.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
$.each(steps.etym.etymTpls, function (name, tpl) {
sel.append($("<option></option>").val(tpl.snippet).text(name + " — " + tpl.desc));
});
sel.on("change", function () {
var snippet = $(this).val();
if (!snippet) return;
var lang = getValues().lang || "";
snippet = snippet.replace(/CODE/g, lang).replace(/SOURCELANG/g, "?");
insertAtCursor(ta[0], snippet);
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(ta);
stepDiv.append(wrap);
initSelects(wrap);
return ta;
},
output: function (values) {
var text = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
if (text) {
return "\n==== Etîmolojî ====\n" + text + "\n";
}
// No etymology entered — emit {{ji?|lang}} as a placeholder.
// Skip it only when every filled sense is a şablon sense
// a completely empty entry with no şablon chosen still gets the placeholder
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasFilledTplSense = dfnSenses.some(function (s) {
return !!s.tpl && !!s.tplTitle;
});
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
if (hasFilledTplSense && !hasRegularMane) return "";
return "\n==== Etîmolojî ====\n{{ji?|" + values.lang + "}}\n";
} },
dunde: {
id: "dunde",
name: "dûnde",
question_fmt: "Eger ev kelîme ji vî zimanî ketibe zimanekî din, vî zimanî û kelîmeya wî binivîse",
visible: function (values) {
return !!values.pos;
},
disabledKeys: function (values) {
return {"navz": true, [values.lang]: true}
},
// Language dropdown + growing word-input list; a new row appends
// when the dropdown gets a value. Mirrors steps.trans, but flat.
buildRow: function (stepDiv) {
var row = $("<li class='wiktku-dunde-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.dunde.keys || [], steps.dunde.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.dunde.onLangChange(stepDiv, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-dunde-words wiktku-lang-row-words'></ul>");
var firstWord = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/build below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-dunde-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Kelîme…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-dunde-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.dunde.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (stepDiv, row) {
var list = stepDiv.find("ul.wiktku-dunde-list");
var isLast = row.is(list.children("li.wiktku-dunde-row:last-child"));
if (isLast) {
var newRow = steps.dunde.buildRow(stepDiv);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across all lang selectors.
steps.dunde.syncDisabledKeys(stepDiv);
},
/** Disables already-selected languages in all other dropdowns. */
syncDisabledKeys: function (stepDiv) {
var taken = $.extend({}, steps.dunde.disabledKeys);
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
stepDiv.find("li.wiktku-dunde-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
build: function (stepDiv) {
if (stepDiv.find("ul.wiktku-dunde-list").length) return null;
var list = $("<ul class='wiktku-dunde-list wiktku-lang-row-list'></ul>");
list.append(steps.dunde.buildRow(stepDiv));
stepDiv.append(list);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return null;
},
/** Reads all [langcode, words[]] rows from the DOM. */
getRows: function (stepDiv) {
var result = [];
stepDiv.find("li.wiktku-dunde-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-dunde-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
output: function () {
var stepDiv = $("#wiktku-start-dunde");
var rows = steps.dunde.getRows(stepDiv);
if (!rows.length) return "";
var lines = $.map(rows, function (row) {
var lang = row[0], words = row[1];
return "* {{dû|" + lang + "|" + words.join("|") + "}}";
});
return "\n==== Dûnde ====\n" + lines.join("\n") + "\n";
}
},
zarava: {
id: "zarava",
name: "bi zaravayên din",
question_fmt: "'"+ pageName + "' di zaravayên din de çawa tê gotin?",
visible: function (values) {
return steps.trans.visible(values);
},
disabledKeys: {},
buildRow: function (group) {
var row = $("<li class='wiktku-zarava-row wiktku-lang-row'></li>");
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zaravayekî bigere");
populateMenus(langSel, steps.zarava.keys || [], steps.zarava.disabledKeys);
langSel.on("change", function () {
steps.zarava.onLangChange(group, row);
});
var wordList = $("<ul class='wiktku-zarava-words wiktku-lang-row-words'></ul>");
var firstWord = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-zarava-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-zarava-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.zarava.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-zarava-list");
var isLast = row.is(list.children("li.wiktku-zarava-row:last-child"));
if (isLast) {
var newRow = steps.zarava.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
steps.zarava.syncDisabledKeys(group);
},
/** Disables already-selected dialects in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.zarava.disabledKeys);
group.find("li.wiktku-zarava-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-zarava-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
/** Builds (or fetches) the .wiktku-zarava-group for one gloss. */
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-zarava-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-zarava-list wiktku-lang-row-list'></ul>");
list.append(steps.zarava.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
/** Syncs one .wiktku-zarava-group per current gloss (same logic as steps.trans.build). */
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-zarava-group").remove();
return null;
}
var existingGroups = stepDiv.find(".wiktku-zarava-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.zarava.buildGroup(stepDiv, gi);
var label = group.find(".wiktku-zarava-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-zarava-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the dialect rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-zarava-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-zarava-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
/** Reads all rows from the DOM, grouped by gloss position. */
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-zarava-group").each(function () {
result.push(steps.zarava.getGroupTranslations($(this)));
});
return result;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-zarava");
var glosses = steps.trans.glossList();
if (!glosses.length) return "";
// A completely empty entry still gets a placeholder section
// to fill in later; an individual placeholder gloss is only
// skipped once some other sense already has real content.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.zarava.getGroupTranslations(group) : [];
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Bi zaravayên din ====\n" + blocks.join("\n") + "\n";
}
},
trans: {
id: "trans",
name: "werger",
question_fmt: "Tercimeyên vê maneyê bi zimanên din binivîse",
visible: function (values) {
if (!(values.lang === "ku" && values.pos)) return false;
// Hide only when every sense is a filled template sense (no plain
// gloss to attach translations to). A mix, or nothing filled in
// yet, still shows Werger.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRealSense = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
var hasFilledTplSense = dfnSenses.some(function (s) {
return s.tpl && s.tplTitle;
});
return hasRealSense || !hasFilledTplSense;
},
disabledKeys: {
"navz": true,
"ku": true,
"ckb": true,
"sdh": true,
"lki": true,
"hac": true,
"zza": true },
// Language dropdown + growing word-input list, scoped to one gloss's
// group (each gloss has its own group and row list).
buildRow: function (group) {
var row = $("<li class='wiktku-trans-row wiktku-lang-row'></li>");
// Language selector — the searchable picker is applied after insertion into DOM.
var langSel = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
langSel.attr("data-placeholder", "Li zimanekî bigere");
populateMenus(langSel, steps.trans.keys || [], steps.trans.disabledKeys);
// When a language is chosen, ensure a next empty row exists.
langSel.on("change", function () {
steps.trans.onLangChange(group, row);
});
// Word list for this language.
var wordList = $("<ul class='wiktku-trans-words wiktku-lang-row-words'></ul>");
var firstWord = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(firstWord));
// The searchable picker is attached by initSelects() once the row
// is in the DOM (see onLangChange/buildGroup below).
row.append(langSel).append(wordList);
return row;
},
makeWordInput: function () {
var input = $("<textarea></textarea>")
.attr("name", "wiktku-trans-word-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.attr("placeholder", "Tercime…");
input.on("input", function () {
var list = $(this).closest("ul.wiktku-trans-words");
var item = $(this).parent();
var val = $.trim($(this).val());
if (val && item.is(list.children("li:last-child"))) {
var next = steps.trans.makeWordInput();
list.append($("<li></li>").append(next));
}
});
return input;
},
onLangChange: function (group, row) {
var list = group.find("ul.wiktku-trans-list");
var isLast = row.is(list.children("li.wiktku-trans-row:last-child"));
if (isLast) {
var newRow = steps.trans.buildRow(group);
list.append(newRow);
// Attach the searchable picker now the row is in the DOM.
initSelects(newRow);
}
// Keep disabled keys in sync across this gloss's lang selectors
// only — the same language can be used for different senses.
steps.trans.syncDisabledKeys(group);
},
/** Disables already-selected languages in all other dropdowns within one group. */
syncDisabledKeys: function (group) {
var taken = $.extend({}, steps.trans.disabledKeys);
group.find("li.wiktku-trans-row select").each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
group.find("li.wiktku-trans-row select").each(function () {
var self = $(this);
var own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (!code) return;
$(this).prop("disabled", taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
},
// Builds (or fetches) the .wiktku-trans-group for one gloss.
buildGroup: function (stepDiv, gi) {
var group = $("<div class='wiktku-trans-group'></div>").attr("data-gloss-pos", gi);
var list = $("<ul class='wiktku-trans-list wiktku-lang-row-list'></ul>");
list.append(steps.trans.buildRow(group));
group.append(list);
stepDiv.append(group);
// Attach the searchable picker now the select is in the DOM.
initSelects(list);
return group;
},
// Syncs one .wiktku-trans-group per current gloss, keyed by
// data-gloss-pos so groups stay matched across rebuilds.
build: function (stepDiv) {
var glosses = steps.trans.glossList();
if (!glosses.length) {
stepDiv.find(".wiktku-trans-group").remove();
return null;
}
// Drop groups for glosses that no longer exist (always from the
// tail — groups are only ever appended in gloss order below).
var existingGroups = stepDiv.find(".wiktku-trans-group");
if (existingGroups.length > glosses.length) {
existingGroups.slice(glosses.length).remove();
}
$.each(glosses, function (gi, gloss) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) group = steps.trans.buildGroup(stepDiv, gi);
// Only label groups when there's more than one gloss to
// distinguish between — a single gloss needs no caption.
var label = group.find(".wiktku-trans-group-label");
if (glosses.length > 1) {
if (!label.length) {
label = $("<div class='wiktku-trans-group-label'></div>");
group.prepend(label);
}
label.text(gloss.index + " " + gloss.text);
} else if (label.length) {
label.remove();
}
});
return null;
},
/** Reads the translation rows for a single gloss group. */
getGroupTranslations: function (group) {
var result = [];
group.find("li.wiktku-trans-row").each(function () {
var lang = $(this).find("select").val();
if (!lang) return;
var words = [];
$(this).find("ul.wiktku-trans-words textarea").each(function () {
var v = $.trim($(this).val());
if (v) words.push(v);
});
if (words.length) result.push([lang, words]);
});
return result;
},
// Reads all translation rows, grouped by gloss position.
getTranslations: function (stepDiv) {
var result = [];
stepDiv.find(".wiktku-trans-group").each(function () {
result.push(steps.trans.getGroupTranslations($(this)));
});
return result;
},
// Builds the numbered "{ index, gloss }" list, one per dfn sense
// with a real mane (skips template senses). index is 1-based and
// matches the sense's position in the "#" definition list.
// When nothing's filled in, returns one placeholder entry so
// Werger still has a group to attach to; when every sense is a
// filled template sense, stays empty (matches trans.visible()).
glossList: function () {
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var glosses = [];
var hasFilledTplSense = false;
$.each(dfnSenses, function (i, s) {
if (s.tpl) {
if (s.tplTitle) hasFilledTplSense = true;
return; // template sense — no gloss to caption with
}
var dfn = s.mane;
if (!dfn) {
// No mane, but other fields are filled — still gets a
// gloss group since dfn.getOutput() emits {{mane?}} for it.
if (senseHasExtraContent(s)) glosses.push({ index: i + 1, text: "" });
return;
}
if (dfn[0] !== dfn[0].toLowerCase()) dfn = decapitalize(dfn);
dfn = stripWikilinks(dfn).replace(/[.…?!)\]،؟]+$/, "");
glosses.push({ index: i + 1, text: dfn });
});
if (!glosses.length && !hasFilledTplSense) {
glosses.push({ index: 1, text: "" });
}
return glosses;
},
output: function (values) {
if (values.lang !== "ku") return "";
var stepDiv = $("#wiktku-start-trans");
var glosses = steps.trans.glossList();
// Every sense is a template sense — there's no real gloss
// anywhere and no group in the DOM either (glossList() only
// returns empty in that case), so skip the section entirely.
if (!glosses.length) return "";
// A completely empty entry (nothing filled in dfn at all, no
// şablon chosen either) still gets a placeholder Werger section
// to fill in later. Only
// skip an individual placeholder gloss below when some other
// sense actually has real content, making that particular
// placeholder redundant.
var dfnSenses = steps.dfn.getSenses($("#wiktku-start-dfn"));
var hasRegularMane = dfnSenses.some(function (s) {
return !s.tpl && (s.mane !== "" || senseHasExtraContent(s));
});
// Each gloss now has its own group of translation rows —
// attach every gloss's own words under its own werger-ser/-bin
// pair instead of dumping everything under the first one.
var blocks = $.map(glosses, function (gloss, gi) {
var group = stepDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
var rows = group.length ? steps.trans.getGroupTranslations(group) : [];
// Placeholder gloss (no real mane entered yet) with nothing
// translated into it. If some other sense already has real
// content, this placeholder is redundant — skip it. If the
// whole entry is empty, keep it so Werger still gets a
// section to fill in.
if (!gloss.text && !rows.length && hasRegularMane) return undefined;
if (rows.length) {
// Sort by Kurdish sort key.
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || {};
var bLang = langsByCode[b[0]] || {};
return (aLang.sortKey || "").localeCompare(bLang.sortKey || "", "ku");
});
}
var wordLines = $.map(rows, function (row) {
var lang = row[0];
var words = row[1];
var wikiWords = words.map(function (w) {
return "{{W|" + lang + "|" + w + "}}";
}).join(", ");
return "* {{Z|" + lang + "}}: " + wikiWords;
}).join("\n");
var block = "{{werger-ser|" + (gloss.text || "?") + "|" + gloss.index + "}}";
if (wordLines) block += "\n" + wordLines;
block += "\n{{werger-bin}}";
return block;
});
if (!blocks.length) return "";
return "\n==== Werger ====\n" + blocks.join("\n") + "\n";
}
},
cavkani: {
id: "cavkani",
name: "çavkanî",
question_fmt: "Çavkaniyên din (ne jêgirtin) lê zêde bike",
visible: function (values) {
return !!values.pos;
},
// Fills the insert-dropdown from the members of [[Kategorî:Çavkanî]].
refreshTplSelect: function () {
var sel = $("#wiktku-start-cavkani .wiktku-tpl-insert-select");
if (!sel.length) return;
sel.find("option[value!='']").remove();
$.each(catSrcTemplates || [], function (_, name) {
sel.append($("<option></option>").val(name).text(name));
});
markTemplateOptions(sel, function (opt) { return opt.val(); });
// Tell the picker the option list changed.
sel.trigger("wiktku:refresh");
},
build: function (stepDiv) {
if (stepDiv.find(".wiktku-cavkani-wrap").length) return null;
var wrap = $("<div class='wiktku-cavkani-wrap'></div>");
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-cavkani-" + $.now())
.attr("class", "wiktku-start-step-field wiktku-cavkani-text")
.attr("rows", 4)
.attr("placeholder", "Şablonekî ji lîsteyê hilbijêre an rasterast binivîse…");
// Template insert dropdown, populated from [[Kategorî:Çavkanî]] —
// options refreshed once the category members are fetched.
var sel = $("<select class='wiktku-tpl-insert-select'></select>");
sel.attr("data-placeholder", "Şablonek lê zêde bike…");
sel.append($("<option value=''>— şablonek lê zêde bike —</option>"));
var status = $("<span class='wiktku-cavkani-status'></span>");
sel.on("change", function () {
var name = $(this).val();
if (!name) return;
insertAtCursor(ta[0], "* {{" + name + "}}\n");
$(this).val("");
sel.trigger("wiktku:refresh");
ta.focus();
});
wrap.append(sel);
wrap.append(status).append(ta);
stepDiv.append(wrap);
// build() runs again for every new beş (see the "+ Beşeke din"
// handler), after the initial page-wide pass — so this select needs
// its own init or it stays a plain, unsearchable native <select>.
initSelects(wrap);
if (catSrcTemplates) {
steps.cavkani.refreshTplSelect();
} else {
status.text("Şablon tên barkirin…");
fetchCatSrcTemplates().then(function () {
steps.cavkani.refreshTplSelect();
status.text("");
}, function () {
status.text("Şablon nehatin barkirin.");
});
}
return ta;
},
output: function () {
var text = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
if (!text) return "";
return "\n==== Çavkanî ====\n" + text + "\n";
}
},
};
steps.trans.keys = steps.lang.choices;
steps.dunde.keys = steps.lang.choices;
steps.zarava.keys = getZaravaKeys(steps.lang.choices);
// This array only controls DOM build order — nothing else keys off it
// (see the comment above stepIdToGroup). Mane tab order: Mane (senses,
// with malûmatên pêşketî nested inside each, see steps.dfn.build) →
// Wêne → Bi zaravayên din → Werger, so dfn/resim/zarava/trans sit
// adjacent to each other in that order.
var ordered_steps = [
steps.lang, steps.pos, steps.posparams,
steps.cinsiyet, steps.jimar, steps.lektur, steps.tr,
// Mane tab, in reading order: the senses, then the two things that
// belong to the entry as a whole rather than to one sense.
steps.dfn, steps.tewandin, steps.alfabeyendin, steps.zarava, steps.trans,
// Etîmolojî tab.
steps.etym, steps.dunde,
// Eleqedar tab — three lists of the same shape.
steps.je, steps.tekildar, steps.binereherwiha,
// Bilêvkirin isn't part of any STEP_GROUPS tab, but still needs a
// slot here because this array is what builds the DOM.
steps.pron,
// Beşên din fields — behind the picker inside the Eleqedar tab.
steps.resim,
steps.bnrh, steps.bikaranin, steps.formalt, steps.hevmane, steps.dijmane, steps.jornav, steps.jernav,
steps.cavkani, steps.xwendin];
// ── Step-group tabs ──────────────────────────────────────────────────────
// Three tabs: "Mane" holds the senses (whose own blocks carry Nimûne,
// Jêgirtin, Hevmane and Dijmane — see steps.dfn.allGroups), Tewandin,
// alfabeyendin, zarava and trans; "Etîmolojî" splits out that topical
// cluster; "Eleqedar" holds je/tekildar/binereherwiha plus — behind the
// "Beşên din" picker, which lives inside this tab rather than as its
// own — everything else (resim, formalt, hevmane, dijmane, jornav,
// jernav, bikaranin, cavkani, xwendin, bnrh). A newly-picked section is
// inserted right after the picker (see renderBesenDinPicker()), which
// sits above je/tekildar/binereherwiha in the DOM — so picking one
// pushes those down rather than the other way around. "mane" stays
// first as the default-active tab since Mane is required content.
// Tewandin keeps its own free-text {{ku-tewîn-nav}} field and does NOT
// absorb the Cins/zayend form parameters (m=/n=/p=), which stay on the
// headword template.
// Steps that live behind the "Beşên din" picker inside Eleqedar — only
// these need an explicit "open" before they're shown, even once
// Eleqedar's tab is active.
var BESENDIN_STEP_IDS = ["resim", "formalt", "hevmane", "dijmane", "jornav", "jernav", "bikaranin", "cavkani", "xwendin", "bnrh"];
var STEP_GROUPS = [
{ id: "mane", label: "Mane", steps: ["dfn", "tewandin", "alfabeyendin", "zarava", "trans"] },
{ id: "etimoloji", label: "Etîmolojî", steps: ["etym", "dunde"] },
{ id: "eleqedar", label: "Eleqedar", steps: ["je", "tekildar", "binereherwiha"].concat(BESENDIN_STEP_IDS) },
];
// Reverse lookup: step id -> index into STEP_GROUPS (undefined for
// ungrouped steps — now only lang/pos/posparams/cinsiyet/lektur/tr,
// which stay always-visible since they're required to get to everything
// else).
var stepIdToGroup = {};
$.each(STEP_GROUPS, function (gi, g) {
$.each(g.steps, function (_, sid) { stepIdToGroup[sid] = gi; });
});
// Step ids that belong to each section (not shared across sections).
var PER_SECTION_STEP_IDS = [
"pos", "posparams", "cinsiyet", "jimar", "lektur", "tr",
"resim", "dfn", "tewandin", "formalt", "bikaranin", "alfabeyendin", "hevmane", "dijmane", "jornav", "jernav", "je", "tekildar", "etym", "dunde", "binereherwiha", "zarava", "trans", "cavkani", "xwendin"
];
// ── Multi-section state ──────────────────────────────────────────────────
// Each entry is a plain snapshot: { pos, cinsiyet, lektur, tr, dfn (senses[]),
// je, tekildar, binereherwiha, trans (rows[]) }.
var sections = [{}]; // section 0 starts empty (DOM is the truth)
var activeSection = 0;
// ── Bilêvkirin as a standalone section ───────────────────────────────
// Pronunciation belongs to the word, not to one part of speech, so it
// sits beside Navdêr/Lêker in the tab bar rather than inside any of them.
// Modelled as a flag rather than an entry in `sections` since
// activeSection is used as an array index in many places — viewing
// Bilêvkirin leaves activeSection pointing at the POS section you were on.
var viewingPron = false; // is it the tab currently on screen
// Which STEP_GROUPS tab is currently shown. Lives out here (like
// activeSection above) so refresh() — which is not nested inside build()
// — can read it and so renderGroupTabs()/updateGroupTabIndicators() below
// can be called from refresh() too. Both operate on the tab bar via its
// #wiktku-group-tabs id rather than a closured element reference, for the
// same reason.
var activeGroup = 0;
// Unsaved-changes / autosave state. Lives out here (same reason as
// activeGroup above) so destroy() — which runs on final submit and on
// switching to the source editor, and is not nested inside build() —
// can clear the timers and the beforeunload warning. build() assigns
// into these; it doesn't re-declare them.
var formDirty = false;
var autosaveDebounceTimer = null;
var autosaveInterval = null;
/** Re-renders the step-group tab bar to reflect the current activeGroup. */
// Scrolls so the given tab bar (by element id) sits at the top of the
// viewport, below the sticky site header/section-tabs offset. Switching
// tabs can leave the viewport wherever it happened to be scrolled in the
// previous tab's (possibly much longer or shorter) content, otherwise —
// the new tab's content might open off-screen entirely.
//
// Switching tabs can also shorten the page (e.g. leaving a long "Beşên
// din" field), and the browser may clamp the scroll position on its own
// timing before our measurement runs. Deferring one frame lets that
// settle first, so the offsetTop read below is the final, post-refresh
// one.
function scrollTabBarIntoView(barId) {
window.requestAnimationFrame(function () {
var bar = document.getElementById(barId);
if (!bar) return;
var form = document.getElementById("wiktku-start-form");
var stabsH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-stabs-h")) || 0) : 0;
var siteHeaderH = form ? (parseFloat(form.style.getPropertyValue("--wiktku-siteheader-h")) || 0) : 0;
var top = bar.getBoundingClientRect().top + window.pageYOffset - stabsH - siteHeaderH;
$("html, body").animate({ scrollTop: Math.max(0, top) }, 200);
});
}
function renderGroupTabs() {
var groupTabsDiv = $("#wiktku-group-tabs");
if (!groupTabsDiv.length) return;
groupTabsDiv.empty();
$.each(STEP_GROUPS, function (i, g) {
var tab = $("<span></span>")
.addClass("wiktku-group-tab")
.attr("data-group-idx", i)
.text(g.label)
.toggleClass("wiktku-tab-active", i === activeGroup);
(function (idx) {
tab.on("click", function () {
if (idx === activeGroup) return;
activeGroup = idx;
renderGroupTabs();
if (idx === ELEQEDAR_GROUP_IDX) renderBesenDinPicker();
refresh();
scrollTabBarIntoView("wiktku-group-tabs");
});
}(i));
groupTabsDiv.append(tab);
});
}
/**
* Toggles a "needs attention" dot on any group tab that still has a
* required, unfilled field hidden inside it — so switching tabs doesn't
* leave the person wondering why "Bişîne" stays disabled.
*/
function updateGroupTabIndicators(values) {
var tabs = $("#wiktku-group-tabs .wiktku-group-tab");
if (!tabs.length) return;
$.each(STEP_GROUPS, function (i, g) {
var missing = false;
$.each(g.steps, function (_, sid) {
var step = steps[sid];
if (!step || !step.required) return;
var val = values[sid];
if (val === "" || (step.multiple && $.isArray(val) && val.join("") === "")) {
missing = true;
}
});
tabs.eq(i).toggleClass("wiktku-tab-missing", missing && i !== activeGroup);
});
}
// "Beşên din" reveals one field at a time via a picker rather than
// stacking them all. Per-section state, snapshotted in
// saveCurrentSection() and restored in restoreSection() below, and
// auto-seeded from any field that already has content (so a returning
// draft doesn't hide something already filled in).
var openedOtherSections = {};
var BESENDIN_SET = {};
$.each(BESENDIN_STEP_IDS, function (_, sid) { BESENDIN_SET[sid] = true; });
// "eleqedar"'s index in STEP_GROUPS — the Beşên din picker now renders
// inside this tab rather than having its own. Derived rather than
// hardcoded — this was once a literal 2 and silently pointed at the
// wrong tab the moment Etîmolojî and Eleqedar were reordered.
var ELEQEDAR_GROUP_IDX = (function () {
for (var i = 0; i < STEP_GROUPS.length; i++) {
if (STEP_GROUPS[i].id === "eleqedar") return i;
}
return STEP_GROUPS.length - 1;
}());
/** Re-renders the "Beşên din" picker to list only the not-yet-opened sections. */
function renderBesenDinPicker() {
var wrap = $("#wiktku-besendin-picker");
if (!wrap.length) return;
wrap.empty();
var pickerValues = getValues();
var unopened = $.grep(BESENDIN_STEP_IDS, function (sid) {
if (openedOtherSections[sid]) return false;
// Don't offer a section that would open onto nothing: several of
// these are gated (Bi zaravayên din / Werger need lang=ku, most
// need a pos). refresh() would hide it again straight away, so
// picking it would read as a broken dropdown.
var step = steps[sid];
if (step && typeof step.visible === "function") {
try {
if (!step.visible(pickerValues)) return false;
} catch (e) { /* a step whose visible() needs DOM we lack yet — offer it */ }
}
return true;
});
if (!unopened.length) return; // everything's open — nothing left to pick
var sel = $("<select class='wiktku-besendin-select'></select>");
sel.attr("data-placeholder", "— binebeşên din hilbijêre —");
sel.append($("<option value=''>— binebeşên din hilbijêre —</option>"));
$.each(unopened, function (_, sid) {
var step = steps[sid];
if (!step) return;
sel.append($("<option></option>").val(sid).text(capitalize(step.name)));
});
sel.on("change", function () {
var sid = $(this).val();
if (!sid) return;
openedOtherSections[sid] = true;
// Newly opened section moves right under the picker, ahead of
// whatever's already open, instead of wherever it happened to
// sit in the fixed step order.
$("#wiktku-start-" + sid).insertAfter(wrap);
renderBesenDinPicker();
refresh();
});
wrap.append(sel);
initSelects(wrap);
}
/** Marks any BESENDIN_STEP_IDS field that already has real content as
* "opened", so restoring a draft doesn't bury filled-in sections behind
* the picker. Reuses each step's own output() as the emptiness check —
* it already knows what "empty" means per field, and it reads the DOM
* the restore just populated. Call this AFTER the DOM has the restored
* values in it. */
function seedOpenedFromContent() {
var values = getValues();
$.each(BESENDIN_STEP_IDS, function (_, sid) {
if (openedOtherSections[sid]) return;
var step = steps[sid];
if (!step || typeof step.output !== "function") return;
try {
if (step.output(values)) openedOtherSections[sid] = true;
} catch (e) { /* a step whose output() needs more state than we have yet — skip it */ }
});
}
function snapshotDfn() {
return steps.dfn.getSenses($("#wiktku-start-dfn"));
}
// Rebuilds the dfn DOM from a previously snapshotted senses array.
function restoreDfn(senses) {
var stepDiv = $("#wiktku-start-dfn");
// Tear down the existing tab bar + list so build() recreates them cleanly.
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
var list = stepDiv.find("ol.wiktku-dfn-list");
$.each(senses, function (i, sense) {
// Ensure enough blocks exist (build() seeds one).
var blocks = list.find("li.wiktku-dfn-sense");
var block = blocks.eq(i);
if (!block.length) {
var nb = steps.dfn.buildSenseBlock(stepDiv);
list.append(nb);
block = list.find("li.wiktku-dfn-sense").last();
}
// Template selector.
block.find("select.wiktku-dfn-tpl-select").val(sense.tpl || "");
if (sense.tpl) {
block.find("textarea.wiktku-dfn-mane").hide();
block.find("input.wiktku-dfn-tpl-title").val(sense.tplTitle || "").show();
// The picker is collapsed behind a link by default; a restored
// sense that already uses a template has to show it, or the
// chosen template is invisible and looks lost.
var reveal = block.data("revealTplRow");
if (typeof reveal === "function") reveal();
} else {
block.find("textarea.wiktku-dfn-mane").val(sense.mane || "").show();
block.find("input.wiktku-dfn-tpl-title").hide().val("");
}
// dfnArea is the only thing actually saved, so split it back into
// tercime/îzah for editing (mirrors syncNonKuToMane's join format).
var maneParts = splitTercimeIzah(sense.mane);
block.find(".wiktku-dfn-tercime").val(maneParts ? maneParts.tercime : (sense.mane || ""));
block.find(".wiktku-dfn-izah").val(maneParts ? maneParts.izah : "");
// Re-sync the formên alternatîv/hevmane/… hide state now that
// tplSelect's value was set (its own "change"
// handler doesn't fire from .val(), so this doesn't happen otherwise).
var updateVis = block.data("updateFieldVisibility");
if (updateVis) updateVis();
// Same for the mane-box mode (plain vs tercime+îzah), since this
// also depends on tplSelect's (and lang's) value.
var updateMane = block.data("updateManeMode");
if (updateMane) updateMane();
// Restore a sub-list (etiket, formên alternatîv, hevmane …) from a values array.
function restoreSubList(cls, values) {
if (!values || !values.length) return;
var groupDiv = block.find("." + cls + "-group");
var subList = groupDiv.find("ol, ul").first();
if (!subList.length) return; // group not found
var useModifiers = !!MODIFIER_SUBGROUPS[cls];
// Fill existing items, append new ones as needed.
$.each(values, function (j, val) {
var items = subList.find("li");
if (j < items.length) {
// useModifiers rows keep the word and its modifier tags in
// separate elements (see makeModifierLi) — setRawValue()
// re-splits val() the same way parsing it fresh would,
// instead of dumping the raw "word<key:value>" string into
// the visible word box.
var setRaw = items.eq(j).data("setRawValue");
if (useModifiers && typeof setRaw === "function") {
setRaw(val);
} else {
items.eq(j).find("textarea").val(val);
}
} else {
var ta = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow")
.val(val);
ta.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(useModifiers ? makeModifierLi(ta) : $("<li></li>").append(ta));
}
});
if (!useModifiers) return;
// A fresh empty row goes on the end so there's always somewhere
// to add the next word without first having to click into a
// filled one.
var filled = subList.find("li.wiktku-word-li");
if (filled.last().is(":last-child")) {
var nextTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-restore-next-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 1)
.attr("class", "wiktku-dfn-subfield wiktku-start-narrow");
nextTa.on("input", function () {
steps.dfn.onSubInput(subList, this, 1, true, useModifiers);
});
subList.append(makeModifierLi(nextTa));
}
}
// Reveal and populate lazy groups that have data.
var lazyMap = {
"wiktku-dfn-alternativ": sense.alternativ,
"wiktku-dfn-hevmane": sense.hevmane,
"wiktku-dfn-dijmane": sense.dijmane,
"wiktku-dfn-koordine": sense.koordine,
"wiktku-dfn-jornav": sense.jornav,
"wiktku-dfn-jernav": sense.jernav
};
$.each(lazyMap, function (cls, vals) {
if (vals && vals.length) restoreSubList(cls, vals);
});
// Dever/Şîve, Etîket, Kategorî — chip fields, not textarea
// sub-lists, restored via the adders buildSenseBlock stashed on
// the block.
var chipRestoreMap = {
addDeverChip: sense.dever,
addEtiketChip: sense.etiket,
addKategoriChip: sense.kategori
};
$.each(chipRestoreMap, function (dataKey, vals) {
var addChip = block.data(dataKey);
if (addChip && vals && vals.length) {
$.each(vals, function (_, v) { addChip(v); });
}
});
// Nimûne items are objects { text, tr?, t? } — restore using the builder.
if (sense.nimune && sense.nimune.length) {
var nimuneGroupDiv = block.find(".wiktku-dfn-nimune-group");
var nimuneList = nimuneGroupDiv.find("ol.wiktku-dfn-nimune-list");
var buildFn = nimuneGroupDiv.data("buildNimuneLi");
nimuneList.empty();
$.each(sense.nimune, function (_, n) {
var text = typeof n === "object" ? (n.text || "") : n;
if (!text) return;
var li = buildFn ? buildFn() : $("<li class='wiktku-dfn-nimune-li'></li>");
li.find(".wiktku-dfn-nimune-text").val(text).each(function () { highlightLinebreak(this); });
if (n.tr) li.find(".wiktku-dfn-nimune-tr").val(n.tr);
if (n.t) li.find(".wiktku-dfn-nimune-t").val(n.t);
nimuneList.append(li);
});
// Always leave a blank trailing item.
if (buildFn) nimuneList.append(buildFn());
}
// Restore jêder entries — accepts current array shape or a legacy
// single object (older saved sections) for backwards compat.
var jederArr = $.isArray(sense.jeder) ? sense.jeder : (sense.jeder ? [sense.jeder] : []);
if (jederArr.length) {
var jederGroupDiv = block.find(".wiktku-dfn-jeder-group");
var jederList = jederGroupDiv.find("ol.wiktku-dfn-jeder-list");
var buildJederFn = jederGroupDiv.data("buildJederLi");
jederList.empty();
$.each(jederArr, function (_, j) {
if (!j || !j.quote) return;
var li = buildJederFn ? buildJederFn() : $("<li class='wiktku-dfn-jeder-li'></li>");
li.find(".wiktku-dfn-nimune-quote").val(j.quote).each(function () { highlightLinebreak(this); });
if (j.quoteTr) li.find(".wiktku-dfn-nimune-quote-tr").val(j.quoteTr);
if (j.quoteT) li.find(".wiktku-dfn-nimune-quote-t").val(j.quoteT);
if (j.jederManual) {
var manualSel = li.find(".wiktku-dfn-quote-src-sel");
manualSel.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val(j.jederManual);
} else if (j.jederCatTpl) {
// Legacy format from before the şablon search switched rows to
// manual mode directly — convert it the same way now, so it's
// editable like any other free-form source.
var catSelEl = li.find(".wiktku-dfn-quote-src-sel");
catSelEl.val("__manual__").trigger("change");
li.find(".wiktku-dfn-quote-src-manual").val("{{" + j.jederCatTpl + "}}");
} else if (j.jederTpl) {
var srcSel = li.find(".wiktku-dfn-quote-src-sel");
srcSel.val(j.jederTpl).trigger("change");
if (j.jêderFields) {
$.each(j.jêderFields, function (key, val) {
li.find(".wiktku-dfn-quote-src-input[data-field-key='" + key + "']").val(val);
});
}
}
jederList.append(li);
});
// Always leave a blank trailing item, same as Nimûne.
if (buildJederFn) jederList.append(buildJederFn());
}
});
// Restoring a draft opens the first sense's tab by default.
steps.dfn.setOpenSense(stepDiv, list.find("li.wiktku-dfn-sense").first());
}
// Snapshots simple (non-dfn) per-section steps from the DOM into obj.
function saveCurrentSection() {
var snap = {};
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
snap.dfn = snapshotDfn();
return;
}
if (id === "etym") {
snap.etym = $.trim($("#wiktku-start-etym .wiktku-etym-text").val());
return;
}
if (id === "cavkani") {
snap.cavkani = $.trim($("#wiktku-start-cavkani .wiktku-cavkani-text").val());
return;
}
if (id === "posparams") {
snap.posparams = $.trim($("#wiktku-start-posparams .wiktku-posparams-text").val());
return;
}
if (id === "resim") {
// One entry per row, including blank ones, so a mid-edit draft
// (e.g. a filename typed but no caption yet) survives a cache
// restore instead of being silently dropped.
snap.resim = $("#wiktku-start-resim li.wiktku-resim-item").map(function () {
return {
file: $.trim($(this).find(".wiktku-resim-file").val()),
caption: $.trim($(this).find(".wiktku-resim-caption").val()),
alt: $.trim($(this).find(".wiktku-resim-alt").val())
};
}).get();
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
if (id === "trans") {
snap.trans = steps.trans.getTranslations(stepDiv);
return;
}
if (id === "zarava") {
snap.zarava = steps.zarava.getTranslations(stepDiv);
return;
}
if (id === "dunde") {
snap.dunde = steps.dunde.getRows(stepDiv);
return;
}
// Generic: read all step fields.
var fields = stepDiv.find(".wiktku-start-step-field");
if (step.multiple) {
snap[id] = fields.map(function () { return $(this).val(); }).get();
} else {
snap[id] = fields.val();
}
});
// Which "Beşên din" sub-sections this section has already opened
// via the picker — per-section, like everything else here.
snap.openedOtherSections = $.extend({}, openedOtherSections);
sections[activeSection] = snap;
}
// Restores a previously saved snapshot into the form DOM.
function restoreSection(snap) {
$.each(PER_SECTION_STEP_IDS, function (_, id) {
if (id === "dfn") {
restoreDfn(snap.dfn || []);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
$("#wiktku-start-etym .wiktku-etym-text").val(snap.etym || "");
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
$("#wiktku-start-cavkani .wiktku-cavkani-text").val(snap.cavkani || "");
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
steps.posparams.build(ppDiv);
var savedTa = snap.posparams || "";
ppDiv.find(".wiktku-posparams-text").val(savedTa);
// Re-parse saved textarea to restore helper widget states.
if (savedTa) {
var cMatch = savedTa.match(/\|c=([^|}\s]+)/);
var jMatch = savedTa.match(/\|j=([^|}\s]+)/);
var trMatch = savedTa.match(/\|tr=([^|}\s]+)/);
// Write into the shared hidden field (not the visible
// select, which has no <option>s yet) — syncHelpers()
// below reads it back to restore the dropdown once the
// gender/number options for this language are populated.
if (cMatch) $("#wiktku-start-cinsiyet-field").val(cMatch[1]);
if (jMatch) $("#wiktku-start-jimar-field").val(jMatch[1]);
if (trMatch) ppDiv.find(".wiktku-posparams-field[data-pp-key='tr']").val(trMatch[1]);
// For lêker, c= holds the verb-type code instead of a
// gender — split it back across the two dropdowns it is
// now assembled from ("x-gh" → form "x" + base "gh").
if (cMatch && /^lêker/.test(snap.pos || "")) {
var lekturParts = /^(?:([xphb])-)?(n?gh)$/.exec(cMatch[1]);
if (lekturParts) {
ppDiv.find(".wiktku-posparams-lektur-sel").val(lekturParts[2]);
ppDiv.find(".wiktku-posparams-lekturform-sel").val(lekturParts[1] || "");
}
}
// Restore checked optional params. "form" is stored as
// "erê" rather than "1" (see rebuildTa), so it needs its
// own check here.
ppDiv.find(".wiktku-posparams-opt-panel input[type='checkbox']").each(function () {
var key = $(this).val();
var expected = key === "form" ? "erê" : "1";
$(this).prop("checked", savedTa.indexOf("|" + key + "=" + expected) !== -1);
});
}
steps.posparams.syncHelpers(ppDiv);
return;
}
if (id === "resim") {
var resimDiv = $("#wiktku-start-resim");
resimDiv.find(".wiktku-resim-wrap").remove();
steps.resim.build(resimDiv);
// Accept both the new array-of-rows format and the older
// single {file, caption} object saved before multi-image
// support existed.
var resimSnap = snap.resim;
var rows = $.isArray(resimSnap) ? resimSnap : (resimSnap ? [resimSnap] : []);
if (rows.length) {
var list = resimDiv.find("ol.wiktku-resim-list");
list.empty();
$.each(rows, function (_, r) {
var item = steps.resim.buildItem(resimDiv);
item.find(".wiktku-resim-file").val(r.file || "");
item.find(".wiktku-resim-caption").val(r.caption || "");
item.find(".wiktku-resim-alt").val(r.alt || "");
list.append(item);
});
// Always keep a blank trailing row, same as Nimûne/Dûnde.
var lastItem = list.children("li.wiktku-resim-item").last();
if ($.trim(lastItem.find(".wiktku-resim-file").val())) {
list.append(steps.resim.buildItem(resimDiv));
}
}
return;
}
if (id === "trans") {
// Rebuild trans groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
steps.trans.build(transDiv);
var groupsSnap = snap.trans || [];
$.each(groupsSnap, function (gi, rows) {
var group = transDiv.find(".wiktku-trans-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-trans-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-trans-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-trans-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.trans.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.trans.makeWordInput()));
}
});
});
return;
}
if (id === "zarava") {
// Rebuild zarava groups (dfn must already be restored by this
// point in the $.each loop, so glossList() reflects it).
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
steps.zarava.build(zaravaDiv);
var zaravaGroupsSnap = snap.zarava || [];
$.each(zaravaGroupsSnap, function (gi, rows) {
var group = zaravaDiv.find(".wiktku-zarava-group[data-gloss-pos='" + gi + "']");
if (!group.length) return; // gloss no longer exists — drop its rows
var list = group.find("ul.wiktku-zarava-list");
$.each(rows, function (j, row) {
var lang = row[0], words = row[1];
var lastRow = list.find("li.wiktku-zarava-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-zarava-words");
$.each(words, function (k, word) {
var wordItems = wordList.find("li");
if (k < wordItems.length) {
wordItems.eq(k).find("textarea").val(word);
} else {
var extraInput = steps.zarava.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.zarava.makeWordInput()));
}
});
});
return;
}
if (id === "dunde") {
// Rebuild dunde rows.
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
steps.dunde.build(dundeDiv);
var dundeRows = snap.dunde || [];
$.each(dundeRows, function (i, row) {
var lang = row[0], words = row[1];
var list = dundeDiv.find("ul.wiktku-dunde-list");
var lastRow = list.find("li.wiktku-dunde-row").last();
lastRow.find("select").val(lang).trigger("change");
var wordList = lastRow.find("ul.wiktku-dunde-words");
$.each(words, function (j, word) {
var wordItems = wordList.find("li");
if (j < wordItems.length) {
wordItems.eq(j).find("textarea").val(word);
} else {
// .val() below doesn't fire "input", so the normal
// auto-grow never runs — append a matching item
// ourselves instead of silently dropping the word.
var extraInput = steps.dunde.makeWordInput();
wordList.append($("<li></li>").append(extraInput));
extraInput.val(word);
}
});
// Leave one trailing empty field so the row can still grow
// when the user types, matching a freshly-built row.
if ($.trim(wordList.children("li").last().find("textarea").val())) {
wordList.append($("<li></li>").append(steps.dunde.makeWordInput()));
}
});
return;
}
var step = steps[id];
if (!step) return;
var stepDiv = $("#wiktku-start-" + id);
var fields = stepDiv.find(".wiktku-start-step-field");
var val = snap[id];
if (val === undefined || val === null) return;
if (step.multiple) {
var arr = val;
fields.each(function (i) {
if (i < arr.length) $(this).val(arr[i]);
});
} else {
fields.val(val);
}
// Tell the pickers about the programmatic value change.
fields.filter("select").trigger("wiktku:refresh");
});
// Restore which "Beşên din" sub-sections were already opened for
// this section, then seed in anything the DOM restore above just
// filled in but that wasn't marked opened yet.
openedOtherSections = $.extend({}, snap.openedOtherSections || {});
seedOpenedFromContent();
renderBesenDinPicker();
}
function getWikitextForCurrentSection(callback) {
var values = getValues();
var entry = steps.pos.output(values);
// Bilêvkirin is NOT emitted here — it is one shared field for the
// whole word, so emitting it per section duplicated it. getWikitext()
// prints it once, above the POS sections.
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.tewandin.output(values);
entry += steps.bikaranin.output(values);
entry += steps.alfabeyendin.output(values);
entry += steps.formalt.output(values);
entry += steps.hevmane.output(values);
entry += steps.dijmane.output(values);
entry += steps.jornav.output(values);
entry += steps.jernav.output(values);
entry += steps.je.output(values);
entry += steps.tekildar.output(values);
entry += steps.etym.output(values);
entry += steps.dunde.output(values);
entry += steps.binereherwiha.output(values);
entry += steps.zarava.output(values);
entry += steps.trans.output(values);
entry += steps.cavkani.output(values);
entry += steps.xwendin.output(values);
callback(entry, values);
});
}
function getWikitextAllSections(callback) {
saveCurrentSection();
var results = new Array(sections.length);
var sharedValues = getValues(); // lang/pron are shared
function processSection(i) {
// Each hop through here can be reached asynchronously: the dfn
// step waits on mw.loader.using("ext.gadget.linkify") before
// calling back, so from that point on the submit handler's own
// try/catch is no longer on the stack. Guard every hop instead,
// or a throw after linkify loads vanishes into an unhandled
// rejection and the form just sits there looking busy.
try {
if (i >= sections.length) {
// All done — restore original active section.
restoreSection(sections[activeSection]);
refresh();
callback(results, sharedValues);
return;
}
restoreSection(sections[i]);
refresh();
getWikitextForCurrentSection(function (sectionText) {
results[i] = sectionText;
processSection(i + 1);
});
} catch (e) {
if (typeof onSubmitError === "function") onSubmitError(e);
else throw e;
}
}
processSection(0);
}
// Assembles wikitext for all sections plus the shared lang header.
function getWikitext(callback) {
getWikitextAllSections(function (sectionTexts, sharedValues) {
var bnrh = steps.bnrh.output(sharedValues);
var entry = steps.lang.output(sharedValues);
// One pronunciation per word, printed once between the language
// header and the POS sections — see steps.pron.output.
entry += steps.pron.output(sharedValues);
entry += sectionTexts.join("\n");
// Reconstruction-namespace pages (ns 108) mark the language
// section with {{jinûvesazkirî|langcode}} directly above it —
// only prepended when generating fresh wikitext, same as h-bnr
// below (addLangMode handles an existing page separately).
if (!addLangMode && ns === 108) entry = "{{jinûvesazkirî|" + sharedValues.lang + "}}\n" + entry;
// h-bnr goes before the language section — only prepended when
// generating fresh wikitext (not addLangMode, which handles it separately).
if (!addLangMode && bnrh) entry = bnrh + entry;
// Edit summary uses section 0's values. Read from the saved snapshot
// (not the live DOM) so it doesn't mix section 0's pos with whichever
// section happens to be active/restored at this point.
var dfnSenses = (sections[0] && sections[0].dfn) || [];
var dfnSummary = dfnSenses.map(function (s) {
if (s.mane) return s.mane;
// Template sense (e.g. {{guharto|ku|somepage}}) — same
// construction as the tplCall built in getOutput, so the
// summary shows what actually gets written to the entry
// instead of going blank.
if (s.tpl && s.tplTitle) {
return s.tplNoLang
? "{{" + s.tpl + "|" + s.tplTitle + "}}"
: "{{" + s.tpl + "|" + sharedValues.lang + "|" + s.tplTitle + "}}";
}
return "";
}).filter(Boolean).join("; ");
var summary = "([[Mediawiki:Gadget-start.js|WF:NÛ]] v.2.0) +" +
sharedValues.lang + ":" + sections[0].pos + ":" + dfnSummary;
var summaryField = document.getElementById("wpSummary");
if (addLangMode && summaryField.value) {
summary = summaryField.value + " " + summary;
}
summaryField.value = summary;
callback(entry);
});
}
var continueBtn;
// Set by build() to its submitFailed(). Lives out here because the
// wikitext-assembly functions below are outside build()'s closure but
// still need a way to report a failure to the person rather than dying
// silently in the console.
var onSubmitError = null;
// Whether Mane/Bi zaravayên din/Werger has anything in it — kept
// outside refresh() so the click handler can read the latest value.
var wiktkuContentGateOk = true;
function getValuesForStep(stepId) {
var fields = $("#wiktku-start-" + stepId + " .wiktku-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktku-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function () {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function populateMenus(inputs, choices, disabledValues, placeholderText) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>").text(placeholderText || ""));
$.each(choices, function (j, choice) {
// Append the language code in parentheses so that typing a code
// (e.g. "ku") in the search box still finds the language name.
var displayText = capitalize(choice.label);
if (choice.sortKey !== undefined) {
// This is a language choice {code, label, sortKey} — show code.
displayText = capitalize(choice.label) + " (" + choice.code + ")";
}
var option = $(mw.html.element("option", {
value: choice.code }, displayText));
if (disabledValues[choice.code]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
// Keys to disable for a key menu: already-taken or explicitly disabled.
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield").each(function () {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktku-start-step-keyfield wiktku-start-narrow" }));
input.attr("data-placeholder", "Hilbijêre " + step.keyName);
populateMenus(input, step.keys, getDisabledKeys(step));
// Keep the key menus mutually exclusive: whatever one row selects is
// disabled in every other row. Recomputed from the DOM on each change.
input.on("change", function () {
var all = $("#wiktku-start-" + step.id + " .wiktku-start-step-keyfield");
var taken = {};
all.each(function () {
var v = $(this).val();
if (v) taken[v] = true;
});
all.not(this).each(function () {
var self = $(this), own = self.val();
self.find("option").each(function () {
var code = $(this).val();
if (code) $(this).prop("disabled", !!taken[code] && code !== own);
});
self.trigger("wiktku:refresh");
});
});
// Return the wrapper; the select lives inside it and still fires change.
return wrapNativeSelect(input);
}
// Adds another text field to the list if the last one now has text.
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $("<textarea></textarea>")
.attr("name", "wiktku-field-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("class", currentInput.attr("class"))
.attr("rows", currentInput.attr("rows") || 1);
if (step.placeholder) {
nextInput.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = step.modifiers
? makeModifierLi(nextInput).prepend(nextKeyInput || "")
: $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) initSelects(nextItem);
}
var canContinue = !continueBtn.isDisabled();
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
// Shows/hides steps based on whether their prerequisites are met, and
// updates placeholders where applicable.
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var canContinue = true;
// Re-filter template options in every sense block whenever lang changes.
$("#wiktku-start-dfn .wiktku-dfn-tpl-select").each(function () {
var fn = $(this).data("updateOptions");
if (typeof fn === "function") fn();
});
// Re-filter pronunciation and etymology template dropdowns when language changes.
steps.pron.refreshTplSelect();
steps.etym.refreshTplSelect();
steps.cavkani.refreshTplSelect();
// Re-render any step's question label that depends on current values
// (e.g. dfn's Werger-section reminder, which only applies to ku).
$.each(steps, function (stepId, step) {
if (typeof step.question_fmt === "function") {
$("#wiktku-start-" + stepId + " .wiktku-start-step-question")
.text(step.question_fmt(values));
}
});
// Pos's definition/example text, right under its dropdown — see
// steps.pos.example and the .wiktku-start-pos-example build above.
$("#wiktku-start-pos .wiktku-start-pos-example").text(steps.pos.example(values));
// Sync every sense block's mane-box mode (plain mane vs tercime+îzah)
// to the current language, in case lang changes after blocks exist.
// Same pass also re-syncs Dever/Şîve's default open/closed state
// (open for ku, closed otherwise — see updateDeverOpenState) and the
// "Malûmatên din" accordion's collapsed-state summary/count,
// in case any of its fields changed since the last sync.
$("#wiktku-start-dfn li.wiktku-dfn-sense").each(function () {
var updateMane = $(this).data("updateManeMode");
if (updateMane) updateMane();
var updateDeverOpen = $(this).data("updateDeverOpenState");
if (updateDeverOpen) updateDeverOpen();
var syncAdv = $(this).data("syncSenseAdv");
if (syncAdv) syncAdv();
});
// Sync tr/t sub-fields in every nimûne item to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-nimune-list li.wiktku-dfn-nimune-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it.
var subWrap = li.find(".wiktku-dfn-nimune-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-tr")
.attr("placeholder", "Transliterasyon…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-nimune-sub");
li.append(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 5)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-t")
.attr("placeholder", "Tercimeya nimûneyê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-nimune-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
// Re-sync the "<label> bi <ziman>…" placeholder on every always-visible
// sub-field list (etîket, kategorî, hevmane, …), since the language may
// not have been chosen yet when these lists were first built, or may
// have changed since.
$("#wiktku-start-dfn .wiktku-dfn-sublist").each(function () {
var upd = $(this).data("updatePlaceholders");
if (typeof upd === "function") upd();
});
// Same idea for word-list steps (jê, têkildar, binêre herwiha, …)
// whose placeholder is a function of the current language.
$.each(steps, function (stepId, step) {
if (typeof step.placeholder === "function") {
$("#wiktku-start-" + stepId + " textarea.wiktku-start-step-field")
.attr("placeholder", step.placeholder());
}
});
// Sync tr/t sub-fields in every jêder citation to the current language.
// tr shown when language needs transliteration; t shown when lang !== "ku".
(function () {
var lang = values.lang || "";
var showTr = needsTransliteration(lang);
var showT = lang && lang !== "ku";
$("#wiktku-start-dfn .wiktku-dfn-jeder-list li.wiktku-dfn-jeder-li").each(function () {
var li = $(this);
// Ensure the sub-wrapper exists when we need it. It must sit right
// after the quote textarea and before the source selector.
var subWrap = li.find(".wiktku-dfn-quote-sub");
// tr field
var trTa = li.find(".wiktku-dfn-nimune-quote-tr");
if (showTr && !trTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// Insert after the quote textarea's resize-wrap (if the manual
// resize handle has already been attached to it), not after the
// bare textarea -- otherwise this lands *inside* that wrap,
// between the textarea and its own grip, breaking jêgirtin's
// handle and nesting the tr/t boxes inside it.
var quoteTa = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap = quoteTa.closest(".wiktku-resize-wrap");
(quoteResizeWrap.length ? quoteResizeWrap : quoteTa).after(subWrap);
}
trTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-tr-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-tr")
.attr("placeholder", "Transliterasyona jêgirtinê…");
subWrap.prepend(trTa);
} else if (!showTr && trTa.length) {
trTa.remove();
}
// t field
var tTa = li.find(".wiktku-dfn-nimune-quote-t");
if (showT && !tTa.length) {
if (!subWrap.length) {
subWrap = $("<div></div>").addClass("wiktku-dfn-quote-sub");
// See the tr-field branch above for why this targets the
// resize-wrap instead of the bare textarea.
var quoteTa2 = li.find(".wiktku-dfn-nimune-quote");
var quoteResizeWrap2 = quoteTa2.closest(".wiktku-resize-wrap");
(quoteResizeWrap2.length ? quoteResizeWrap2 : quoteTa2).after(subWrap);
}
tTa = $("<textarea></textarea>")
.attr("name", "wiktku-dfn-nimune-quote-t-" + $.now() + "-" + Math.random().toString(36).substr(2, 9))
.attr("rows", 2)
.attr("class", "wiktku-dfn-subfield wiktku-dfn-nimune-quote-t")
.attr("placeholder", "Tercimeya jêgirtinê bi kurmancî…");
subWrap.append(tTa);
} else if (!showT && tTa.length) {
tTa.remove();
}
// Remove the wrapper if it ended up empty.
subWrap = li.find(".wiktku-dfn-quote-sub");
if (subWrap.length && !subWrap.children().length) subWrap.remove();
});
}());
$.each(steps, function (stepId, step) {
var groupIdx = stepIdToGroup[stepId];
var hiddenByTab = (groupIdx !== undefined && groupIdx !== activeGroup) ||
// Beşên din picker: stays hidden even while its tab is
// active until explicitly opened from the dropdown.
(BESENDIN_SET[stepId] && !openedOtherSections[stepId]);
// Bilêvkirin is its own section now, so the two views are
// mutually exclusive: on it, only pron shows (plus Ziman, which
// is shared and stays above every section); off it, pron is
// hidden no matter which tab is active. pron is deliberately
// ungrouped — stepIdToGroup has no entry for it — so without
// this branch it would show on all four tabs at once.
if (stepId === "pron") {
hiddenByTab = hiddenByTab || !viewingPron;
} else if (viewingPron && stepId !== "lang") {
hiddenByTab = true;
}
if (hiddenByTab || (typeof(step.visible) === "function" && !step.visible(values))) {
$("#wiktku-start-" + stepId).hide();
}
else {
var stepDiv = $("#wiktku-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
// After build, sync the posparams helper widgets whenever pos or lang changes.
if (step.id === "posparams" && typeof step.syncHelpers === "function") {
step.syncHelpers(stepDiv);
}
var input = stepDiv.find(".wiktku-start-step-field");
if (typeof(step.example) === "function") {
// .find() above already reaches nested fields (e.g. the dfn
// step's mane textareas inside each sense block), so one
// assignment covers every step; dfn keeps its own fallback.
var exampleText = step.example(values) || (step.id === "dfn" ? "Mane…" : "");
input.attr("placeholder", exampleText);
}
stepDiv.slideDown();
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
// The three content tabs belong to a POS section — Bilêvkirin has no
// sub-tabs of its own, so the bar goes away while it's on screen.
$("#wiktku-group-tabs").toggle(!!values.pos && !viewingPron);
var besenDinActive = activeGroup === ELEQEDAR_GROUP_IDX && !viewingPron;
$("#wiktku-besendin-picker").toggle(besenDinActive);
// The option list is filtered by each step's visible(), which depends
// on lang/pos — so it has to be rebuilt when those change, not just
// when a section is opened. Safe to call from here: rendering the
// picker doesn't itself call refresh(), only picking an option does.
if (besenDinActive) renderBesenDinPicker();
updateGroupTabIndicators(values);
continueBtn.setDisabled(!canContinue);
// Soft gate: stays a real, clickable OOUI button — a hard
// setDisabled(true) here would swallow the click entirely, leaving
// no way to tell the person why nothing happened. The CSS-only
// "looks disabled" class lets the click handler still run and show
// wiktkuNotify's explanation.
wiktkuContentGateOk = !canContinue || hasMinRequiredContent(values);
continueBtn.$element.toggleClass("wiktku-btn-soft-disabled", !wiktkuContentGateOk);
}
function destroy() {
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
if (autosaveInterval) clearInterval(autosaveInterval);
formDirty = false;
$(window).off("beforeunload.wiktkuUnsaved");
$("#wiktku-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard").slideDown();
// #editform was kept laid-out but off-screen (see build()) instead of
// display:none, precisely so the editor never had to size itself
// against a zero-width/height box. Just bring it back on-screen —
// no slideDown needed since it was never actually collapsed.
$("#editform").removeClass("wiktku-offscreen");
// Belt-and-braces: if WikiEditor/CodeMirror/mobile's auto-grow still
// cached a stale size from some earlier point, nudge them to
// recalculate now that the box is genuinely visible.
$(window).trigger("resize");
if (window.CodeMirror) {
var cm = $("#wpTextbox1").data("CodeMirror") || $("#wpTextbox1").data("cmInstance");
if (cm && typeof cm.refresh === "function") cm.refresh();
}
$("#ca-edit").removeClass("selected");
$("#ca-wiktku-nowizard").addClass("selected");
}
// Priority order for Kurdish dialects and navz.
// Codes listed here come before all other languages; unlisted codes
// are sorted alphabetically by their display name via localeCompare.
var LANG_PRIORITY = ["navz", "ku", "hac", "sdh", "lki", "ckb", "zza"];
// Inserts a new "== {{ziman|code}} ==" section at the correct sorted
// position: navz, then ku/hac/sdh/lki/ckb/zza, then alphabetical by name.
function insertNewLangSection(existing, wikitext) {
var newCodeMatch = wikitext.match(new RegExp("^" + langHeaderSource("[^}]+")));
var newCode = newCodeMatch ? newCodeMatch[1] : "";
var sections = [];
var preamble = "";
var anyHeaderSrc = langHeaderSource("[^}]+");
var firstMatch = new RegExp("^" + anyHeaderSrc, "m").exec(existing);
if (!firstMatch) {
// No existing lang sections — just write the new section.
// Add \n\n only when there is real non-whitespace content above.
var trimmed = existing.replace(/\s+$/, "");
return trimmed ? trimmed + "\n\n" + wikitext : wikitext;
}
// Everything before the first == {{ziman|...}} == is the preamble.
var firstSectionStart = firstMatch.index;
// Walk back to the start of that line.
var lineStart = existing.lastIndexOf("\n", firstSectionStart - 1);
var preambleEnd = lineStart !== -1 ? lineStart + 1 : 0;
preamble = existing.slice(0, preambleEnd);
// Parse each language section: header line + everything until the next header.
var body = existing.slice(preambleEnd);
// (?![\s\S]) matches only true end-of-string, since a bare "$" with
// /m would stop at the section's first line break instead.
var langSectionRe = new RegExp(
"^(" + anyHeaderSrc + "\\n?)([\\s\\S]*?)(?=\\n" + anyHeaderSrc + "|(?![\\s\\S]))",
"gm"
);
var m;
while ((m = langSectionRe.exec(body)) !== null) {
sections.push({
code: m[2],
content: m[0] // full text of this section including header
});
}
// Determine sort rank: priority list first (by index), then alphabetical.
function getLangName(code) {
var entry = langsByCode[code];
return entry ? entry.label : code;
}
function sortRank(code) {
var idx = LANG_PRIORITY.indexOf(code);
return idx !== -1 ? idx : LANG_PRIORITY.length;
}
function compareLangs(a, b) {
var ra = sortRank(a), rb = sortRank(b);
if (ra !== rb) return ra - rb;
// Both are outside priority list — sort by display name.
return getLangName(a).localeCompare(getLangName(b), userLang);
}
// Insert the new section into the sorted list.
var newSection = { code: newCode, content: wikitext };
var inserted = false;
for (var i = 0; i < sections.length; i++) {
if (compareLangs(newCode, sections[i].code) < 0) {
sections.splice(i, 0, newSection);
inserted = true;
break;
}
}
if (!inserted) sections.push(newSection);
// Re-join: preamble + sections, each separated by exactly \n\n.
// Trim trailing whitespace off each section's content first so we
// control the spacing precisely.
var joined = sections.map(function (s) {
return s.content.replace(/\s+$/, "");
}).join("\n\n");
// Only add a separator after the preamble when it has real content.
var preambleTrimmed = preamble.replace(/\s+$/, "");
var prefix = preambleTrimmed ? preambleTrimmed + "\n\n" : "";
return prefix + joined + "\n";
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktku-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Edîtora çavkaniyê veke",
"ca-wiktku-nowizard", "Vê rûpelê bi edîtora çavkaniyê çêke",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
if (callToAction.length) {
callToAction.text(origCallToActionText +
" Forma li jêr ji bo çêkirina madeyeke nû bi kar bîne.");
}
var startForm = $("<form id='wiktku-start-form'></form>");
var clearPageCheck = null;
if (addLangMode) {
startForm.append($("<p class='wiktku-addlang-notice'></p>").html(
"Bi vê formê tu yê zimanekî nû li rûpelê îlawe bikî. Eger ev ziman jixwe hebe, beşa nû li dawiya vî zimanî dê were îlawekirin. " +
"Ji kerema xwe netîceya dawî kontrol bike, eger problemek derkeve, li <a href='" +
mw.util.getUrl('Mediawiki talk:Gadget-start.js') +
"'>nîqaşa vê amûrê</a> problemê ragihîne."
));
clearPageCheck = $("<input type='checkbox' id='wiktku-clear-page-check'>");
var clearPageLabel = $("<label for='wiktku-clear-page-check'></label>")
.text(" Muhtewaya rûpelê paqij bike (her tiştî jê bibe û rûpelê ji nû ve binivîse)");
startForm.append(
$("<p class='wiktku-addlang-notice'></p>")
.append(clearPageCheck)
.append(clearPageLabel)
);
// Re-evaluate step visibility (e.g. the pron step) whenever this
// is toggled, since it changes what counts as "existing content".
clearPageCheck.on("change", function () {
refresh();
});
}
var spinner;
// Tab bar for switching between POS sections.
var sectionTabsDiv = $("<div id='wiktku-section-tabs'></div>");
// Tab bar for switching between step-groups (topics) within a section.
// renderGroupTabs()/updateGroupTabIndicators() (which populate and
// maintain it) live outside build() — see near "var activeSection".
var groupTabsDiv = $("<div id='wiktku-group-tabs'></div>");
// Both tab bars are sticky (CSS). group-tabs' sticky "top" has to sit
// right below section-tabs' actual rendered height, which isn't a
// fixed number since the bar can wrap onto a second line. Measured
// here and published as a CSS custom property the stylesheet reads
// back; recomputed on content changes and window resize.
//
// Vector 2022 has its own sticky header (.vector-sticky-header) that
// pins to the real top:0 once you scroll past the page title, which
// would otherwise sit directly behind (and hide) our section-tabs.
// Fixed by measuring how much of the viewport Vector's header
// currently occupies and offsetting both our bars below it, via
// --wiktku-siteheader-h. Vector's header slides in/out with scroll
// position, so this is re-measured on scroll too, not just resize.
function siteHeaderOffset() {
var el = document.querySelector(".vector-sticky-header");
if (!el) return 0;
var rect = el.getBoundingClientRect();
// Present in the DOM at all times on Vector 2022, but only
// actually occupying screen space once scrolled into place —
// before that it sits translated above the viewport (negative/
// zero rect), which must not add a phantom gap under our bars.
if (rect.bottom <= 0 || rect.top > 4) return 0;
return Math.max(0, rect.bottom);
}
var stickyOffsetQueued = false;
function syncStickyTabsOffset() {
var h = sectionTabsDiv.is(":visible") ? (sectionTabsDiv.outerHeight() || 0) : 0;
startForm.css("--wiktku-stabs-h", h + "px");
startForm.css("--wiktku-siteheader-h", siteHeaderOffset() + "px");
}
function queueStickyTabsOffsetSync() {
if (stickyOffsetQueued) return;
stickyOffsetQueued = true;
window.requestAnimationFrame(function () {
stickyOffsetQueued = false;
syncStickyTabsOffset();
});
}
$(window).on("resize.wiktkuStickyTabs scroll.wiktkuStickyTabs", queueStickyTabsOffsetSync);
// "Beşên din" picker — sits right under the group tab bar, only
// shown while the Eleqedar tab is active (toggled in refresh(), see
// ELEQEDAR_GROUP_IDX); renderBesenDinPicker() fills it.
var besenDinPickerDiv = $("<div id='wiktku-besendin-picker' class='wiktku-besendin-picker'></div>");
/** Puts the form back in a usable state after a failed submit and
* says what happened. Without this the button stays disabled and
* the spinner spins forever on any exception — the person is left
* staring at a form that looks busy but is dead, with the reason
* visible only in the browser console. */
function submitFailed(err) {
spinner.hide();
continueBtn.setDisabled(false);
continueBtn.$element.removeClass("wiktku-btn-sending");
var detail = (err && (err.message || err.toString())) || "sedem nediyar";
mw.notify(
"Şandin bi ser neket: " + detail +
" — tiştê ku te nivîsî winda nebûye. Heke ev dubare bibe, " +
"bi 'Edîtora çavkaniyê veke' berdewam bike.",
{ type: "error", tag: "wiktku-submit", autoHide: false }
);
// Deliberately console.error as well: the notification is for the
// person, this is for whoever they report the bug to.
if (window.console && console.error) console.error("[wiktku] submit failed", err);
}
onSubmitError = submitFailed;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.setDisabled(true);
continueBtn.$element.addClass("wiktku-btn-sending");
spinner.show();
try {
getWikitext(function (wikitext) {
try {
if (addLangMode && clearPageCheck && clearPageCheck.prop("checked")) {
// User asked to wipe the page — write fresh content.
$("#wpTextbox1").val(wikitext);
} else if (addLangMode) {
var textbox = $("#wpTextbox1");
var existing = textbox.val() || "";
// ── h-bnr merge ──────────────────────────────────────────
// If the user filled bnr-h inputs, either merge into the
// existing {{h-bnr|…}} call or prepend a new one.
var bnrhOut = steps.bnrh.output(getValues());
if (bnrhOut) {
var newPages = bnrhOut.replace(/^\{\{h-bnr\|/, "").replace(/\}\}\n?$/, "").split("|");
var bnrhRe = /\{\{h-bnr\|([^}]*)\}\}/i;
if (bnrhRe.test(existing)) {
existing = existing.replace(bnrhRe, function (_, inner) {
var existingPages = inner.split("|").map(function (p) { return p.trim(); });
$.each(newPages, function (_, p) {
p = p.trim();
if (p && existingPages.indexOf(p) === -1) existingPages.push(p);
});
return "{{h-bnr|" + existingPages.join("|") + "}}";
});
} else {
existing = bnrhOut + existing;
}
}
// If this language's section already exists (whitespace
// differences aside), merge into it; otherwise insert new.
var langHeaderMatch = wikitext.match(new RegExp("^(" + langHeaderSource("[^}]+") + "\\n)"));
if (langHeaderMatch) {
var langHeader = langHeaderMatch[1]; // e.g. "== {{ziman|ku}} ==\n"
var newCode = langHeaderMatch[2];
var existingHeader = findLangHeader(existing, newCode);
if (existingHeader) {
// Strip the header from the new output.
var newContent = wikitext.slice(langHeader.length);
// Find the insertion point: end of the existing lang section.
// A new L2 heading marks the start of the next language block.
var searchFrom = existingHeader.index + existingHeader.match.length;
// Find the next == … == heading after this section's start.
var nextL2 = existing.indexOf("\n== ", searchFrom);
var insertAt = nextL2 !== -1 ? nextL2 : existing.length;
// Ensure there's a blank line before the inserted block.
var before = existing.slice(0, insertAt);
var after = existing.slice(insertAt);
var sep = /\n\s*$/.test(before) ? "" : "\n";
textbox.val(before + sep + newContent + after);
} else {
// New language — insert at the correct sorted position.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
// Unexpected format — insert at sorted position anyway.
textbox.val(insertNewLangSection(existing, wikitext));
}
} else {
$("#wpTextbox1").val(wikitext);
}
$("html, body").animate({scrollTop: 0});
callToAction.text("Hindik ma!");
// The draft is now live in the editor, submitted or not — keeping
// it cached would just offer to restore already-used content
// the next time the wizard opens on this page.
try { formCache.Set({}); } catch (e) { /* storage unavailable — harmless, the content is already in the editor */ }
destroy();
} catch (e) {
// Thrown while writing the result into #wpTextbox1.
submitFailed(e);
}
});
} catch (e) {
// Catches anything thrown synchronously inside getWikitext.
submitFailed(e);
}
});
// Tracks whether groupTabsDiv has been inserted yet.
var groupTabsInserted = false;
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktku-start-step",
id: "wiktku-start-" + step.id }));
var name = capitalize(step.name);
var nameH3 = $("<h3></h3>").addClass("wiktku-start-step-name");
var nameLabel = $("<label></label>")
.attr("for", "wiktku-start-" + step.id + "-field")
.text(name);
nameH3.append(nameLabel);
stepDiv.append(nameH3);
if (step.question_fmt) {
// question_fmt can be a plain string, or a function of the
// current values (used by steps whose question text depends
// on e.g. the chosen language) — refresh() keeps it in sync
// afterwards via the .wiktku-start-step-question class.
var questionText = typeof step.question_fmt === "function"
? step.question_fmt(getValues())
: step.question_fmt;
var questionLabel = $(mw.html.element("label", {
"class": "wiktku-start-step-question",
"for": "wiktku-start-" + step.id + "-field" }, questionText));
stepDiv.append(questionLabel);
}
var input;
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktku-start-step-field",
id: "wiktku-start-" + step.id + "-field" }));
input.attr("data-placeholder",
capitalize(step.name) + " hilbijêre ");
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
// initSelects() runs over the whole form once it is in the DOM.
stepDiv.append(input);
// Pos-specific: the definition/example for whichever cureyê
// rêzimanî is currently chosen, as plain text right under the
// dropdown — kept in sync by refresh() via .wiktku-start-pos-example.
if (step.id === "pos") {
stepDiv.append(
$("<p class='wiktku-step-hint wiktku-start-pos-example'></p>").text(step.example(getValues()))
);
}
} else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
} else {
input = $("<textarea></textarea>")
.attr("class", "wiktku-start-step-field")
.attr("id", "wiktku-start-" + step.id + "-field")
.attr("rows", step.rows || 2);
if (step.narrow) input.addClass("wiktku-start-narrow");
if (step.placeholder) {
input.attr("placeholder", typeof step.placeholder === "function" ? step.placeholder() : step.placeholder);
}
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
// Same one-box-per-word layout as the per-sense ⊕-modifier
// groups (see makeModifierLi) when this step has modifiers.
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append(step.modifiers
? makeModifierLi(input)
: $("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
// Section tabs (switch between POS sections) sit above the
// per-section fields, starting at "pos".
if (step.id === "pos") {
startForm.append(sectionTabsDiv);
}
// Step-group tabs sit just before the first grouped step in
// ordered_steps. Derived from stepIdToGroup so this keeps
// working no matter how STEP_GROUPS/ordered_steps get reordered.
if (!groupTabsInserted && stepIdToGroup[step.id] !== undefined) {
startForm.append(groupTabsDiv);
startForm.append(besenDinPickerDiv);
groupTabsInserted = true;
}
startForm.append(stepDiv);
});
// ── Section tab helpers ──────────────────────────────────────────────
// ── Add-section button — lives in the tab bar, disabled until pos chosen ──
var addSectionBtn = new OO.ui.ButtonWidget({
label: "+ Beşeke din",
disabled: true,
classes: [ "wiktku-add-sense-btn" ]
});
/** Adds a new, empty POS section and switches to it. */
function addPosSection() {
saveCurrentSection();
viewingPron = false;
sections.push({});
activeSection = sections.length - 1;
$.each(PER_SECTION_STEP_IDS, function (_, id) {
// Declared once here rather than with `var` in the "dfn"
// branch and again in the fallback branch below — each
// branch returns before the other runs, but both are
// lexically in this same function scope.
var stepDiv;
if (id === "dfn") {
stepDiv = $("#wiktku-start-dfn");
stepDiv.find(".wiktku-dfn-sense-tabs").remove();
stepDiv.find("ol.wiktku-dfn-list").remove();
steps.dfn.build(stepDiv);
return;
}
if (id === "etym") {
$("#wiktku-start-etym .wiktku-etym-wrap").remove();
steps.etym.build($("#wiktku-start-etym"));
return;
}
if (id === "cavkani") {
$("#wiktku-start-cavkani .wiktku-cavkani-wrap").remove();
steps.cavkani.build($("#wiktku-start-cavkani"));
return;
}
if (id === "posparams") {
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-wrap").remove();
return;
}
if (id === "resim") {
$("#wiktku-start-resim .wiktku-resim-wrap").remove();
return;
}
if (id === "trans") {
var transDiv = $("#wiktku-start-trans");
transDiv.find(".wiktku-trans-group").remove();
return;
}
if (id === "zarava") {
var zaravaDiv = $("#wiktku-start-zarava");
zaravaDiv.find(".wiktku-zarava-group").remove();
return;
}
if (id === "dunde") {
var dundeDiv = $("#wiktku-start-dunde");
dundeDiv.find("ul.wiktku-dunde-list").remove();
return;
}
stepDiv = $("#wiktku-start-" + id);
stepDiv.find(".wiktku-start-step-field").val("");
stepDiv.find("select.wiktku-start-step-field").trigger("wiktku:refresh");
});
// Fresh section — no "Beşên din" sub-section has been opened yet.
openedOtherSections = {};
renderBesenDinPicker();
renderSectionTabs();
refresh();
}
addSectionBtn.on("click", addPosSection);
/** Removes one POS section. Assumes the caller already confirmed and
* already checked more than one section remains. If the removed
* section was the one on screen, its in-progress DOM edits are
* discarded along with it (never saved back into sections[]) and a
* neighboring section is restored in its place. */
function removePosSection(idx) {
sections.splice(idx, 1);
if (idx === activeSection) {
viewingPron = false;
activeSection = Math.min(idx, sections.length - 1);
restoreSection(sections[activeSection]);
refresh();
} else if (idx < activeSection) {
activeSection -= 1;
}
renderSectionTabs();
}
/** Re-render the tab bar to reflect current sections[] and activeSection. */
function renderSectionTabs() {
// Detach (not remove) the button first: .empty() strips bound
// event data from any children left inside it, which would
// silently kill addSectionBtn's click handler on every re-render.
addSectionBtn.$element.detach();
sectionTabsDiv.empty();
var lang = getValues().lang || "";
if (!lang) {
sectionTabsDiv.hide();
syncStickyTabsOffset();
return;
}
sectionTabsDiv.show();
// Bilêvkirin leads the bar, ahead of the POS sections.
var pronTab = $("<span></span>")
.addClass("wiktku-section-tab wiktku-section-tab-pron")
.text("Bilêvkirin")
.toggleClass("wiktku-tab-active", viewingPron);
pronTab.on("click", function () {
if (viewingPron) return;
saveCurrentSection();
viewingPron = true;
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
sectionTabsDiv.append(pronTab);
$.each(sections, function (i) {
var posVal = sections[i].pos || "";
var tabLabel = posVal ? capitalize(posVal) : ("Beş " + (i + 1));
var tab = $("<span></span>")
.addClass("wiktku-section-tab")
.text(tabLabel)
.toggleClass("wiktku-tab-active", i === activeSection && !viewingPron);
(function (idx) {
tab.on("click", function () {
if (idx === activeSection && !viewingPron) return;
// Leaving Bilêvkirin doesn't need a save — its field
// is shared and never part of a section snapshot.
if (!viewingPron) saveCurrentSection();
viewingPron = false;
activeSection = idx;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
scrollTabBarIntoView("wiktku-section-tabs");
});
}(i));
sectionTabsDiv.append(tab);
});
// One delete button for the whole bar — not one per tab — same
// reasoning as the Mane tabs' trash button: deletes whichever
// Beş is currently open, so there's only one thing a click on it
// can possibly remove. Not shown on Bilêvkirin (nothing to
// delete there) or when it's the only Beş left.
if (sections.length > 1 && !viewingPron) {
var activeTabLabel = sections[activeSection] && sections[activeSection].pos
? capitalize(sections[activeSection].pos)
: ("Beş " + (activeSection + 1));
var sectionTrash = makeTrashButton("Vê beşê jê bibe (" + activeTabLabel + ")");
sectionTrash.on("click", function () {
if (!window.confirm("Tu dixwazî \"" + activeTabLabel + "\" jê bibî? Ev tişt nayê vegerandin.")) return;
removePosSection(activeSection);
});
sectionTabsDiv.append(sectionTrash.$element);
}
// Enable "+ Beşeke din" only when the active section has a pos chosen.
var activePos = sections[activeSection] && sections[activeSection].pos
? sections[activeSection].pos
: getValues().pos;
addSectionBtn.setDisabled(!activePos);
sectionTabsDiv.append(addSectionBtn.$element);
syncStickyTabsOffset();
}
renderSectionTabs();
/**
* Re-populates a dynamically-growing "multiple" field list (bnrh, je,
* tekildar, …) from a saved array of strings. Sets each value via
* .val() + a triggered "input" event rather than touching the DOM
* structure directly, so the normal refreshForTextField() growth
* logic adds exactly as many rows as needed — the same thing that
* happens when a person types into the last (empty) row.
*/
function restoreMultiField(id, values) {
var container = $("#wiktku-start-" + id);
$.each(values || [], function (i, v) {
var field = container.find(".wiktku-start-step-field").eq(i);
if (!field.length || !v) return;
field.val(v).trigger("input");
});
}
/**
* Restores a full cached draft: shared lang/pron/bnrh fields, then
* every section (via the same restoreSection()/refresh()/
* renderSectionTabs() sequence already used when switching tabs).
* @param cached {{lang: string, pron: string, bnrh: Array, sections: Array, activeSection: number}}
*/
function restoreFromCache(cached) {
if (cached.lang) {
$("#wiktku-start-lang-field").val(cached.lang);
$("#wiktku-start-lang-field").trigger("wiktku:refresh");
}
// Rebuild dependent steps (pos, pron wrap, etc.) now that lang is set.
refresh();
if (cached.pron !== undefined) {
$("#wiktku-start-pron .wiktku-pron-text").val(cached.pron);
}
if (cached.bnrh) {
restoreMultiField("bnrh", cached.bnrh);
}
sections = (cached.sections && cached.sections.length) ? cached.sections : [{}];
activeSection = (typeof cached.activeSection === "number" &&
cached.activeSection >= 0 && cached.activeSection < sections.length)
? cached.activeSection : 0;
restoreSection(sections[activeSection]);
refresh();
renderSectionTabs();
updateLastSavedIndicator(cached.savedAt || null);
formDirty = false;
wiktkuNotify("Danûstandina berê ya qeydkirî ji cache hate barkirin.",
{ type: "info", tag: "wiktku-cache-restore" });
}
// Update the active tab's label whenever pos changes.
startForm.on("change", "#wiktku-start-pos-field", function () {
// renderSectionTabs() labels each tab from sections[i].pos, which
// is only synced from the DOM by saveCurrentSection() (on tab
// switch/section add). Without this, the active tab's own label
// stays on the previous pos (or "Beş N") until the user switches
// away and back. Update it directly so the rename is immediate.
if (sections[activeSection]) sections[activeSection].pos = $(this).val();
renderSectionTabs();
// A new cureyê rêzimanî means any previously chosen cinsiyet/
// cureyê lêkerê no longer applies — clear both selections (and
// the shared hidden cinsiyet field, since rebuildTa()/syncHelpers()
// fall back to it) before wiping the textarea, so nothing stale
// gets carried over into the rebuilt template.
var ppDiv = $("#wiktku-start-posparams");
ppDiv.find(".wiktku-posparams-cinsiyet-sel").val("");
ppDiv.find(".wiktku-posparams-jimar-sel").val("");
ppDiv.find(".wiktku-posparams-lektur-sel").prop("selectedIndex", 0);
ppDiv.find(".wiktku-posparams-lekturform-sel").prop("selectedIndex", 0);
$("#wiktku-start-cinsiyet-field").val("");
$("#wiktku-start-jimar-field").val("");
ppDiv.find(".wiktku-posparams-text").val("");
// refresh() runs steps.posparams.syncHelpers(), which toggles the
// param rows for the new pos and — since the textarea is now
// empty — calls rebuildTa() to repopulate it.
refresh();
});
// Show the tab bar as soon as a language is selected.
startForm.on("change", "#wiktku-start-lang-field", function () {
renderSectionTabs();
});
var footer = $("<div id='wiktku-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktku-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Tê barkirin…",
title: "Tê barkirin…" }));
footer.append(spinner.hide());
continueBtn = new OO.ui.ButtonWidget({
id: "wiktku-unhide-editform",
label: "Bişîne",
flags: [ "primary", "progressive" ]
});
continueBtn.on("click", function () {
// Immediate feedback that the click registered
continueBtn.$element.addClass("wiktku-btn-flash");
setTimeout(function () {
continueBtn.$element.removeClass("wiktku-btn-flash");
}, 500);
// Ziman/Cureyê rêzimanî already block submission via the real
// OOUI setDisabled (see canContinue in refresh()) — this is a
// second, softer check that still lets the click through so we
// can explain why, instead of the button just silently doing nothing.
if (!wiktkuContentGateOk) {
wiktkuNotify(
"Berî şandinê, tiştekî binivîse: Mane, Bi zaravayên din, an jî Werger — kêmasî yek ji van sê beşan divê tijî be.",
{ type: "warn", tag: "wiktku-mincontent" }
);
return;
}
wiktkuNotify("Tê şandin…", { type: "info", tag: "wiktku-submit" });
startForm.trigger("submit");
});
footer.append(continueBtn.$element);
// "Edîtora çavkaniyê veke" button — inline fallback for the portlet link
var sourceEditorBtn = new OO.ui.ButtonWidget({
label: "Edîtora çavkaniyê veke",
flags: [ "destructive" ],
});
sourceEditorBtn.on("click", function () {
destroy();
});
footer.append(sourceEditorBtn.$element);
// ── Cache save/clear buttons ─────────────────────────────────────────
// Manual, page-scoped save of the entire wizard (all sections, not
// just the active one) via [[MediaWiki:Gadget-StorageUtils.js]]'s
// ObjectStorage. Restoring happens silently on load at the end of
// build(), once the full language list is available.
//
// Saving also happens automatically (debounced while typing, plus a
// periodic fallback) — see "Autosave" below — since relying on the
// person to remember to click "Li cache qeyd bike" before an
// accidental tab close/reload defeats the point of having a cache
// at all.
function buildCacheSnapshot() {
return {
lang: getValues().lang || "",
pron: $("#wiktku-start-pron .wiktku-pron-text").val() || "",
// bnrh is shared (page-level), same as lang/pron above — not
// per-section, so it doesn't live in sections[].
bnrh: getValuesForStep("bnrh"),
sections: sections,
activeSection: activeSection,
savedAt: Date.now()
};
}
// ── Last-saved indicator ─────────────────────────────────────────────
var lastSavedEl = $("<span id='wiktku-last-saved'></span>");
function formatSavedTime(ts) {
try {
return new Date(ts).toLocaleTimeString(mw.config.get("wgUserLanguage"),
{ hour: "2-digit", minute: "2-digit" });
} catch (e) {
return new Date(ts).toLocaleTimeString();
}
}
function updateLastSavedIndicator(ts) {
lastSavedEl.text(ts ? ("Cache: qeydkirî di saet " + formatSavedTime(ts) + " de") : "");
}
// ── Dirty-state tracking + autosave ──────────────────────────────────
// formDirty mirrors "does the live form differ from what's in cache".
// Anything the person types/selects sets it; a successful save (auto
// or manual) clears it; destroy() (final submit, or switching to the
// source editor) also clears it since the wizard's job is over at
// that point either way. formDirty/autosaveDebounceTimer/autosaveInterval
// are declared in the outer scope (near activeGroup) — assigned here,
// not re-declared — so destroy() can reach them too.
var AUTOSAVE_DEBOUNCE_MS = 8000; // save shortly after typing pauses
var AUTOSAVE_INTERVAL_MS = 60000; // …and at least this often regardless
function performAutosave() {
if (!formDirty) return;
saveCurrentSection();
try {
formCache.Set(buildCacheSnapshot());
formDirty = false;
updateLastSavedIndicator(Date.now());
} catch (e) {
// Silent on autosave failure (storage blocked/full, etc.) — the
// manual button still surfaces a proper error if the person
// tries it directly, no need to interrupt typing with a toast.
}
}
function scheduleAutosave() {
formDirty = true;
if (autosaveDebounceTimer) clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = setTimeout(performAutosave, AUTOSAVE_DEBOUNCE_MS);
}
startForm.on("input change", "input, textarea, select", scheduleAutosave);
autosaveInterval = setInterval(performAutosave, AUTOSAVE_INTERVAL_MS);
// Warn on accidental tab close/reload while there's anything unsaved.
$(window).on("beforeunload.wiktkuUnsaved", function (e) {
if (!formDirty) return undefined;
e.preventDefault();
e.returnValue = "";
return "";
});
var saveCacheBtn = new OO.ui.ButtonWidget({
label: "Li cache qeyd bike",
title: "Halê vê formê yê niha (ziman, bilêvkirin û hemû beş) li cache (localStorage) qeyd bike, da ku eger rûpel bêyî xwestin were girtin an ji nû ve were barkirin, tu karibî berdewam bikî."
});
saveCacheBtn.on("click", function () {
saveCurrentSection();
// ObjectStorage.Set() doesn't return a success/failure value, so
// the only way to detect a real failure (storage blocked, quota
// exceeded, JSON.stringify choking on something unexpected) is to
// catch it here rather than inspect a (nonexistent) return value.
try {
var snapshot = buildCacheSnapshot();
formCache.Set(snapshot);
formDirty = false;
updateLastSavedIndicator(snapshot.savedAt);
wiktkuNotify("Halê formê li cache hate qeydkirin.", { type: "success", tag: "wiktku-cache-save" });
} catch (e) {
mw.notify("Qeydkirina li cache bi ser neket.", { type: "error", tag: "wiktku-cache-save" });
}
});
var clearCacheBtn = new OO.ui.ButtonWidget({
label: "Cache paqij bike",
flags: [ "destructive" ],
title: "Datayên qeydkirî yên vê rûpelê ji cache jê bibe (forma ku niha xuya dike naguherîne)."
});
clearCacheBtn.on("click", function () {
formCache.Set({});
// Cancel any autosave already scheduled from typing before this
// click — left alone, it fires a few seconds later (or the
// unconditional periodic autosave does, within a minute) using
// the still-unblanked live form, silently re-writing the very
// cache just cleared. That's why "clear cache, refresh later"
// could still show the old data. Not marking the form dirty here
// means autosave stays off until the person actually edits
// something again, at which point it resumes normally.
if (autosaveDebounceTimer) {
clearTimeout(autosaveDebounceTimer);
autosaveDebounceTimer = null;
}
formDirty = false;
updateLastSavedIndicator(null);
wiktkuNotify("Cache hate paqijkirin.", { type: "success", tag: "wiktku-cache-clear" });
});
// ── "⋯" overflow ─────────────────────────────────────────────
// One primary action and one secondary stay on the bar; everything
// else moves in here, to avoid four same-weight buttons crowding
// (and wrapping to three rows on a phone).
var moreBtn = $("<button type='button' class='wiktku-more-btn' title='Vebijarkên din'>⋯</button>");
var moreMenu = $("<div class='wiktku-more-menu'></div>").hide();
/** Moves an existing OOUI button into the overflow menu, keeping its
* handlers — the widgets already carry the behaviour, so this is
* purely about where they are shown. */
function intoMenu(btn, extraClass) {
var row = $("<div class='wiktku-more-item'></div>");
if (extraClass) row.addClass(extraClass);
btn.$element.on("click", function () { moreMenu.hide(); });
row.append(btn.$element);
moreMenu.append(row);
}
intoMenu(saveCacheBtn);
intoMenu(clearCacheBtn, "wiktku-more-item-danger");
moreMenu.append($("<div class='wiktku-more-sep'></div>"));
moreMenu.append($("<div class='wiktku-more-item wiktku-more-note'></div>").append(lastSavedEl));
moreBtn.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var open = moreMenu.is(":visible");
moreMenu.toggle(!open);
if (!open) {
setTimeout(function () {
$(document).one("click.wiktkuMore", function () { moreMenu.hide(); });
}, 0);
}
});
var moreWrap = $("<span class='wiktku-more-wrap'></span>").append(moreBtn).append(moreMenu);
footer.append(moreWrap);
// Icon-only settings button — opens the Tercîh dialog. Plain OOUI
// icon names depend on which icon-pack module happens to be loaded,
// so this uses the actual Codex "settings" (color-subtle) SVG from
// Commons directly, the same way the spinner above hotlinks its gif.
var prefsBtn = $("<button></button>", {
type: "button",
"class": "wiktku-prefs-btn",
title: "Tercîh — sazkariyên vê amûrê biguherîne"
});
prefsBtn.on("click", function (e) {
e.preventDefault();
wiktkuOpenPrefsDialog();
});
// The gear is icon-only on the bar; inside a text menu it needs a
// label like everything else around it.
prefsBtn.on("click", function () { moreMenu.hide(); });
moreMenu.prepend($("<div class='wiktku-more-sep'></div>"));
moreMenu.prepend($("<div class='wiktku-more-item wiktku-more-item-prefs'></div>")
.append(prefsBtn)
.append($("<span class='wiktku-more-item-label'></span>").text("Tercîh")));
startForm.append(footer);
// NOTE: deliberately NOT .hide() (display:none). WikiEditor/CodeMirror/
// the mobile editor's auto-grow logic measure #wpTextbox1's real
// dimensions during their own init, which happens while the wizard is
// up. A display:none ancestor reports 0 for offsetWidth/offsetHeight/
// scrollHeight, so those scripts bake in a near-zero height that never
// gets recalculated later — slideDown() in destroy() only animates the
// outer box, it doesn't re-trigger their sizing. Pushing #editform
// off-screen instead keeps it laid out (real width/height available to
// measure) without being visible, so the editbox opens at full size.
$("#editform").addClass("wiktku-offscreen").before(startForm);
// renderGroupTabs() looks up #wiktku-group-tabs via a live DOM
// selector, so it must run after startForm is actually in the document.
renderGroupTabs();
renderBesenDinPicker();
// One pass over the whole form: every select that hasn't already been
// initialised, and isn't opted out with .wiktku-plain-select, gets
// the searchable picker.
initSelects(startForm);
refresh();
// Give every draggable textarea a custom resize grip (works
// consistently on Safari/iOS, unlike the native corner grip — see
// the "Manual drag-to-resize handle" comment in styles.css), and
// keep applying it to textareas added later on (new sense blocks,
// quote-source fields, nimûne rows, …).
wiktkuInitManualResize(startForm);
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$(mutation.addedNodes).each(function () {
if (this.nodeType !== 1) return;
wiktkuInitManualResize(this);
});
});
}).observe(startForm[0], { childList: true, subtree: true });
// Now that the full language list is loaded, silently restore any
// draft the editor previously saved for this page.
var cachedDraft = formCache.Get();
if (cachedDraft && cachedDraft.sections && cachedDraft.sections.length) {
restoreFromCache(cachedDraft);
}
}
// Only oojs-ui-core blocks the initial render now — icons-moderation
// (the trash glyph) is fetched lazily by makeTrashButton() instead, so
// slow connections aren't held up loading it before anyone needs it.
mw.loader.using(["oojs-ui-core"], function () {
function wiktkuProceedWithData(wiktkuData) {
var d = wiktkuData || {};
// Populate metadata.
if (d.metadata) $.extend(metadata, d.metadata);
// Populate gender labels.
if (d.genderLabels) $.extend(GENDER_BASE_LABELS, d.genderLabels);
// Jimar (number) code definitions — same shape as the wiki's
// own Lua number-type table (code -> {cat, display[, req]}).
if (d.numberLabels) $.extend(NUMBER_LABELS, d.numberLabels);
// Populate lang choices (pinned list).
if (d.langChoices) steps.lang.choices = choicesFromObject(d.langChoices, true);
// Populate langsByCode and merge full language list from d.ziman.
// d.ziman is a {code: name} object with all known languages.
$.each(steps.lang.choices, function (i, choice) {
if (choice.sortKey === undefined) choice.sortKey = choice.label;
langsByCode[choice.code] = choice;
});
if (d.ziman) {
var pinnedCodes = {};
$.each(steps.lang.choices, function (i, c) { pinnedCodes[c.code] = true; });
var extraChoices = [];
$.each(d.ziman, function (code, name) {
if (/-pro$/i.test(code)) return;
if (/ziman[eê]\s+destan/i.test(name)) return;
if (pinnedCodes[code]) return;
var choice = { code: code, label: name, sortKey: name };
extraChoices.push(choice);
langsByCode[code] = choice;
});
extraChoices.sort(function (a, b) {
return a.sortKey.localeCompare(b.sortKey, userLang);
});
steps.lang.choices = steps.lang.choices.concat(extraChoices);
}
// Keep the full merged list around for trans/dunde/zarava — those
// should still be able to reference any language even when the
// Tercîh panel restricts the main "ziman" step below — then, if
// the user set a restriction there, narrow steps.lang.choices
// itself to just those codes.
var wiktkuFullLangChoices = steps.lang.choices;
if (wiktkuPrefs.langCodes && wiktkuPrefs.langCodes.length) {
var wiktkuAllowedLangSet = {};
$.each(wiktkuPrefs.langCodes, function (i, code) { wiktkuAllowedLangSet[code] = true; });
var wiktkuRestrictedChoices = $.grep(wiktkuFullLangChoices, function (c) {
return !!wiktkuAllowedLangSet[c.code];
});
// Fall back to the full list instead of showing an empty
// dropdown if none of the saved codes matched anything
// (typo'd code, data.json changed since, …).
steps.lang.choices = wiktkuRestrictedChoices.length ? wiktkuRestrictedChoices : wiktkuFullLangChoices;
}
// Populate POS choices — also a plain {code: label} object in data.json.
if (d.posChoices) steps.pos.choices = choicesFromObject(d.posChoices, false);
// Populate template definitions for the dfn sense-block builder.
if (d.tplDefs) steps.dfn.tplDefs = d.tplDefs;
// Populate pos placeholder examples.
if (d.posExamples) steps.pos.posExamples = d.posExamples;
// Populate citation template definitions for the quote widget.
if (d.jederDefs) steps.dfn.jederDefs = d.jederDefs;
// Populate pronunciation and etymology template insert lists.
if (d.pronTpls) {
steps.pron.pronTpls = d.pronTpls;
steps.pron.refreshTplSelect();
}
if (d.etymTpls) {
steps.etym.etymTpls = d.etymTpls;
steps.etym.refreshTplSelect();
}
// Sync trans/dunde/zarava keys with the full language list
// (not the possibly Tercîh-restricted steps.lang.choices).
steps.trans.keys = wiktkuFullLangChoices;
steps.dunde.keys = wiktkuFullLangChoices;
steps.zarava.keys = getZaravaKeys(wiktkuFullLangChoices);
build();
}
// data.json ships inside this package module, so it is already parsed
// and in memory — no action=raw round trip, and no Testing switch to
// forget to flip back before deploying.
wiktkuProceedWithData(require("./start/data.json"));
});
});
//</nowiki>
sb2m3h52ympw90z459r8320q3cxm053
Bikarhêner:Wikihez/Gadget-test/data.json
2
1358587
6334227
6333735
2026-08-16T20:51:04Z
Wikihez
7344
6334227
json
application/json
{
"langChoices": {
"ku": "kurmancî",
"zza": "zazakî",
"ckb": "soranî",
"sdh": "kurdiya başûrî",
"hac": "hewramî",
"de": "almanî",
"ar": "erebî",
"fa": "farisî",
"fr": "fransî",
"fi": "fînî",
"en": "îngilîzî",
"ja": "japonî",
"sv": "swêdî",
"tr": "tirkî"
},
"posChoices": {
"navdêr": "Navdêr (îsm)",
"lêker": "Lêker (fiîl)",
"rengdêr": "Rengdêr (sifet)",
"hoker": "Hoker (zerf)",
"serenav": "Serenav (îsmê xisûsî)",
"artîkel": "Artîkel",
"baneşan": "Baneşan (nîda)",
"bazinedaçek": "Bazinedaçek",
"cînav": "Cînav (zemîr)",
"daçek": "Daçek (edat)",
"girêdek": "Girêdek",
"gotineke pêşiyan": "Gotineke pêşiyan",
"hejmar": "Hejmar",
"hevok": "Hevok (cimle)",
"îfade": "Îfade",
"kurtenav": "Kurtenav",
"navgir": "Navgir",
"paşdaçek": "Paşdaçek",
"paşgir": "Paşgir",
"pêşdaçek": "Pêşdaçek",
"pêşgir": "Pêşgir",
"pirtik": "Pirtik",
"reh": "Reh",
"sembol": "Sembol",
"tîp": "Tîp (herf)"
},
"posExamples": {
"navdêr": "Kelîmeyên ji bo tiştan (sêv, heywan, qedeh) yan terman (dilşadî, bextiyarî hwd.) di rêzimanê de.",
"lêker": "Kelîmeyên ji bo kirin an bûyînê wekî 'nivîsîn' yan 'xeber dan'.",
"rengdêr": "Kelîmeyên ku wesfa navdêran nîşan didin wekî 'sêva sor' yan 'xwarina şîrîn'.",
"hoker": "Kelîmeyên ku dem, cih yan awayê kirinê diyar dikin wekî 'bi vî awayî' yan 'demkî'.",
"daçek": "Kelîmeyên ku cih, dem yan hereketê diyar dikin wekî 'di.. de' yan 'ji'.",
"cînav": "Kelîmeyên ku li şûna navdêran tên bikaranîn daku navdêr neyên dubarekirin wekî 'ew' yan 'em'.",
"baneşan": "Kelîmeyên ku hest, deng hwd. îfade dikin wekî 'noşî!' yan 'aferîn'.",
"serenav": "Navdêrên ku şexsiyetên ferdî nîşan didin, wek navên kes, cih an organîzasyonan nimûne 'London' yan 'Diyarbekir'.",
"gotineke pêşiyan": "Gotinên ku di nav xelkê de bi berbelavî tên bikaranîn bo nimûne 'Serê neêşe ne hewceyî destmalê ye'."
},
"genderLabels": {
"m": "mê",
"n": "nêr",
"nt": "nêtar",
"mn": "mê yan nêr",
"mnmane": "mê yan nêr li gorî maneyê",
"mndudil": "mê yan nêr bikaranîn dudil e",
"g": "giştî",
"y": "yekjimar",
"p": "pirjimar"
},
"tplDefs": {
"rastnivîs": {
"langs": [
"ku"
],
"noLang": true
},
"ku-Latn": {
"langs": [
"ku"
],
"noLang": true
},
"ku-tewîna-nav": {
"langs": [
"ku"
],
"noLang": true
},
"sdh-Arab": {
"langs": [
"sdh"
],
"noLang": true
},
"hac-Arab": {
"langs": [
"hac"
],
"noLang": true
},
"ja-romanîzekirin": {
"langs": [
"ja"
],
"noLang": true
},
"ckb-Arab": {
"langs": [
"ckb"
],
"noLang": true
},
"zza-awayê-Jacobsonê": {
"langs": [
"zza"
]
},
"awayekî din": {
"langs": null
},
"guharto": {
"langs": null
},
"hevmaneya": {
"langs": null
},
"kevnbûyî": {
"langs": null
},
"kêmpeyda": {
"langs": null
},
"kurtkirin": {
"langs": null
},
"paşnav": {
"langs": null
},
"şaşnivîs": {
"langs": null
}
},
"jederDefs": {
"jêder-kitêb": {
"label": "Kitêb",
"fields": {
"nivîskar": "Nivîskar",
"sernav": "Sernav",
"sernavê orjînal": "Sernavê orjînal",
"terciman": "Terciman",
"weşanxane": "Weşanxane",
"cih": "Cih",
"sal": "Sal",
"çap": "Çap",
"rûpel": "Rûpel",
"isbn": "ISBN",
"url": "URL"
}
},
"jêder-kovar": {
"label": "Kovar",
"fields": {
"nivîskar": "Nivîskar",
"nivîskar2": "Nivîskar 2",
"sernav": "Sernav",
"sernavê orjînal": "Sernavê orjînal",
"kovar": "Kovar",
"terciman": "Terciman",
"weşanxane": "Weşanxane",
"cild": "Cild",
"hejmar": "Hejmar",
"sal": "Sal",
"rûpel": "Rûpel",
"url": "URL"
}
},
"jêder-websîte": {
"label": "Websîte",
"fields": {
"nivîskar": "Nivîskar",
"sernav": "Sernav",
"terciman": "Terciman",
"websîte": "Websîte",
"url": "URL",
"arşîv": "Arşîva URLê",
"sal": "Sal",
"tarîx": "Tarîx"
}
}
},
"pronTpls": {
"ku-IPA": {
"desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)",
"snippet": "* {{ku-IPA}}",
"langs": [
"ku"
],
"noLang": true
},
"ku-kîte": {
"desc": "Hecekirina otomatîk ji bo kurmancî",
"snippet": "* {{ku-kîte}}",
"langs": [
"ku"
],
"noLang": true
},
"zza-IPA": {
"desc": "IPAya otomatîk ji bo zazakî",
"snippet": "* {{zza-IPA}}",
"langs": [
"zza"
],
"noLang": true
},
"zza-kîte": {
"desc": "Hecekirina otomatîk ji bo zazakî",
"snippet": "* {{zza-kîte}}",
"langs": [
"zza"
],
"noLang": true
},
"IPA": {
"desc": "Alfabeya fonetîk a navmiletî (IPA)",
"snippet": "* {{IPA|CODE|/…/}}"
},
"deng": {
"desc": "Nimûneya telafûzê bi dosyeya dengî",
"snippet": "* {{deng|CODE|navê_dosyayê.ogg|dever=}}"
},
"bilêv": {
"desc": "{{bilêv|herf|nerm, req|hejmar}}",
"snippet": "* {{bilêv|herf|cure|hejmar}}"
},
"kîte": {
"desc": "Şablona hecekirinê",
"snippet": "* {{kîte|CODE|he|ce}}"
},
"hevdeng": {
"desc": "Hevdeng — gotinên bi heman dengî",
"snippet": "* {{hevdeng|CODE|…}}"
}
},
"etymTpls": {
"ji": {
"desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}",
"snippet": "{{ji|SOURCELANG|CODE|…}}"
},
"deyn": {
"desc": "Deynkirî — {{deyn|lang|kod|kelîme}}",
"snippet": "{{deyn|SOURCELANG|CODE|…}}"
},
"mîr": {
"desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}",
"snippet": "{{mîr|SOURCELANG|CODE|…}}"
},
"qertaf": {
"desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}",
"snippet": "{{qertaf|CODE|…|…}}"
},
"q": {
"desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve",
"snippet": "{{q|CODE|…}}"
},
"hv": {
"desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{hv|SOURCELANG|…}}"
},
"nh": {
"desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{nh|SOURCELANG|…}}"
},
"cotreh": {
"desc": "Cotreh — teror {{cotreh|ku|tirs}}",
"snippet": "{{cotreh|CODE|…}}"
},
"yekser": {
"desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}",
"snippet": "{{yekser|SOURCELANG|CODE|…}}"
},
"wergirtin": {
"desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}",
"snippet": "{{wergirtin|SOURCELANG|CODE|…}}"
},
"paşvesazî": {
"desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}",
"snippet": "{{paşvesazî|CODE|…}}"
},
"qusandin": {
"desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}",
"snippet": "{{qusandin|CODE|…}}"
},
"têkilkirin": {
"desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}",
"snippet": "{{têkilkirin|CODE|…}}"
},
"nyz": {
"desc": "Nayê zanîn — {{nyz|ku|hûr=1}}",
"snippet": "{{nyz|CODE}}"
},
"onom": {
"desc": "Onomatopoetîk — {{onom|ku|hûr=1}}",
"snippet": "{{onom|CODE}}"
},
"navmiletî": {
"desc": "Navmiletî — {{navmiletî|ku|hûr=1}}",
"snippet": "{{navmiletî|CODE}}"
},
"neo": {
"desc": "neolojîzm — {{neolojîzm|ku|hûr=1}}",
"snippet": "{{neolojîzm|CODE}}"
}
},
"metadata": {
"aa": {
"haswikt": 1,
"sc": [
"Latn",
"Ethi"
]
},
"ab": {
"haswikt": 1,
"sc": [
"Cyrl",
"Latn",
"Geor"
]
},
"aer": {
"sc": "Latn"
},
"af": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ak": {
"haswikt": 1
},
"akk": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Xsux"
},
"als": {
"haswikt": 1
},
"am": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1,
"sc": "Ethi"
},
"an": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ang": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"ar": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"alt": 1,
"haswikt": 1,
"sc": "Arab"
},
"arc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": [
"Hebr",
"Syrc"
]
},
"are": {},
"arz": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Arab"
},
"as": {
"haswikt": 1,
"sc": "Beng"
},
"ast": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"av": {
"haswikt": 1
},
"ay": {
"haswikt": 1
},
"az": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl",
"Arab"
]
},
"ba": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"bar": {},
"bat-smg": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"bbl": {
"sc": "Geor"
},
"be": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"be-x-old": {
"sc": "Cyrl"
},
"bg": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"bh": {
"haswikt": 1,
"sc": "Deva"
},
"bhb": {
"sc": "Deva"
},
"bi": {
"haswikt": 1
},
"blt": {
"sc": "Tavt"
},
"bm": {
"haswikt": 1,
"sc": [
"Latn",
"Nkoo",
"Arab"
]
},
"bn": {
"haswikt": 1,
"sc": "Beng"
},
"bo": {
"haswikt": 1,
"sc": "Tibt"
},
"br": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ca": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"cdo": {
"sc": "Hani"
},
"ce": {
"sc": "Cyrl"
},
"ch": {
"haswikt": 1
},
"chr": {
"haswikt": 1,
"sc": "Cher"
},
"cjy": {
"sc": "Hani"
},
"ckb": {
"haswikt": 1,
"sc": "Arab",
"wsc": "ku-Arab"
},
"cmn": {
"sc": "Hani",
"wiktprefix": "zh"
},
"co": {
"haswikt": 1
},
"cpx": {
"sc": "Hani"
},
"cr": {
"haswikt": 1,
"sc": "Cans"
},
"crh": {},
"cs": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"csb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"cu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": [
"Cyrs",
"Glag"
]
},
"cv": {
"sc": "Cyrl"
},
"cy": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"czh": {
"sc": "Hani"
},
"czo": {
"sc": "Hani"
},
"da": {
"g": [
"g",
"nt",
"g-p",
"nt-p",
"p"
],
"haswikt": 1
},
"dax": {},
"de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"dhg": {},
"djb": {},
"dji": {},
"djr": {},
"dng": {
"sc": "Cyrl"
},
"dsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"dsx": {},
"duj": {},
"dv": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Thaa"
},
"dz": {
"haswikt": 1,
"sc": "Tibt"
},
"el": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"en": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"eo": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"es": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"et": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ett": {
"g": [
"y",
"p"
],
"sc": "Ital"
},
"eu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fa": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-cls": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-ira": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fi": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fil": {},
"fj": {
"haswikt": 1
},
"fo": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1
},
"fr": {
"g": [
"n",
"m",
"mnmane",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"frm": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fro": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fur": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fy": {
"g": [
"g",
"nt",
"p"
],
"haswikt": 1
},
"ga": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"gan": {
"sc": "Hani"
},
"gd": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gez": {
"sc": "Ethi"
},
"gl": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gmw-ecg": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gmy": {
"sc": "Linb"
},
"gn": {
"haswikt": 1
},
"gnn": {
"sc": "Latn"
},
"got": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"sc": "Goth"
},
"grc": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
]
},
"gsw": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Gujr"
},
"guf": {
"sc": "Latn"
},
"gv": {
"haswikt": 1
},
"ha": {
"haswikt": 1
},
"hak": {
"sc": "Hani"
},
"har": {
"sc": "Ethi"
},
"he": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"hi": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Deva"
},
"hif": {
"sc": [
"Latn",
"Deva"
]
},
"hit": {
"sc": "Xsux"
},
"hrx": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"hsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"hsn": {
"sc": "Hani"
},
"hu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"hy": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Armn"
},
"ia": {
"haswikt": 1
},
"id": {
"haswikt": 1
},
"ie": {
"haswikt": 1
},
"ik": {
"haswikt": 1
},
"ike": {
"sc": "Cans"
},
"ikt": {
"sc": "Cans"
},
"io": {
"haswikt": 1
},
"is": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"it": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"iu": {
"haswikt": 1,
"sc": "Cans"
},
"ja": {
"haswikt": 1,
"sc": "Jpan"
},
"jay": {
"sc": "Latn"
},
"jbo": {
"haswikt": 1
},
"jv": {
"haswikt": 1
},
"ka": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"khb": {
"sc": [
"Talu",
"Lana",
"Thai"
]
},
"kjh": {
"sc": "Cyrl"
},
"kk": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"kkh": {
"sc": [
"Lana",
"Thai"
]
},
"kl": {
"haswikt": 1
},
"km": {
"haswikt": 1,
"sc": "Khmr"
},
"kn": {
"haswikt": 1,
"sc": "Knda"
},
"ku": {
"g": [
"n",
"m",
"mnmane",
"mndudil",
"n-p",
"m-p",
"p"
],
"wiktprefix": "ku"
},
"ko": {
"haswikt": 1,
"sc": "Kore"
},
"krc": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"ks": {
"haswikt": 1,
"sc": [
"Arab",
"Deva"
],
"wsc": "ks-Arab"
},
"kw": {
"haswikt": 1
},
"ky": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"la": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"lb": {
"g": [
"n",
"m",
"nt",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"lez": {
"sc": "Cyrl"
},
"li": {
"haswikt": 1
},
"lki": {
"wiktprefix": "ku"
},
"lld": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lmo": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ln": {
"haswikt": 1
},
"lo": {
"haswikt": 1,
"sc": "Laoo"
},
"lt": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lv": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lzz": {
"sc": "Geor"
},
"mg": {
"haswikt": 1
},
"mh": {
"haswikt": 1
},
"mi": {
"g": 0,
"haswikt": 1
},
"mk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ml": {
"haswikt": 1,
"sc": "Mlym"
},
"mn": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Cyrl",
"Mong"
]
},
"mnp": {
"sc": "Hani"
},
"mo": {
"haswikt": 1,
"sc": "Cyrl"
},
"mol": {
"sc": "Cyrl"
},
"mr": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1,
"sc": "Deva"
},
"ms": {
"haswikt": 1,
"sc": [
"Latn",
"Arab"
]
},
"mt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"mwp": {
"sc": "Latn"
},
"my": {
"haswikt": 1,
"sc": "Mymr"
},
"na": {
"haswikt": 1
},
"nah": {
"haswikt": 1
},
"nan": {
"wiktprefix": "zh-min-nan",
"sc": "Hani"
},
"nb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"wiktprefix": "no"
},
"nds-de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"nds-nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"ne": {
"haswikt": 1,
"sc": "Deva"
},
"nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nn": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"no": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nod": {
"sc": "Lana"
},
"non": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"oc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"om": {
"haswikt": 1
},
"or": {
"haswikt": 1,
"sc": "Orya"
},
"orv": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrs"
},
"osc": {
"sc": "Ital"
},
"ota": {
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "ota-Arab"
},
"pa": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": [
"Guru",
"Arab"
]
},
"pdt": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"peo": {
"sc": "Xpeo"
},
"phn": {
"sc": "Phnx"
},
"pi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"pjt": {
"sc": "Latn"
},
"pl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"pms": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"pox": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"împf",
"pf"
]
},
"prs": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"ps": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ps-Arab"
},
"pt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"qu": {
"haswikt": 1
},
"rgn": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"rit": {
"sc": "Latn"
},
"rm": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"rn": {
"haswikt": 1
},
"ro": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"roa-rup": {
"haswikt": 1
},
"ru": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"rue": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrl"
},
"ruo": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rup": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "roa-rup"
},
"ruq": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rw": {
"haswikt": 1
},
"sa": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Deva"
},
"sah": {
"sc": "Cyrl"
},
"sc": {
"haswikt": 1
},
"scn": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"sco": {
"sc": "Latn"
},
"sd": {
"haswikt": 1,
"sc": "Arab",
"wsc": "sd-Arab"
},
"sdh": {
"sc": "Arab",
"wiktprefix": "ku"
},
"sg": {
"haswikt": 1
},
"sh": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"si": {
"haswikt": 1,
"sc": "Sinh"
},
"simple": {
"haswikt": 1
},
"sk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sl": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sm": {
"haswikt": 1
},
"sn": {
"haswikt": 1
},
"so": {
"haswikt": 1
},
"spx": {
"sc": "Ital"
},
"sq": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ss": {
"haswikt": 1
},
"st": {
"haswikt": 1
},
"su": {
"haswikt": 1
},
"sux": {
"sc": "Xsux"
},
"sv": {
"g": [
"g",
"nt",
"g-p",
"nt-p"
],
"haswikt": 1
},
"sva": {
"sc": "Geor"
},
"sw": {
"nclass": 1,
"haswikt": 1
},
"syc": {
"sc": "Syrc"
},
"syr": {
"sc": "Syrc"
},
"szl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"ta": {
"haswikt": 1,
"sc": "Taml"
},
"tdd": {
"sc": "Tale"
},
"te": {
"haswikt": 1,
"sc": "Telu"
},
"tg": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Cyrl"
},
"th": {
"haswikt": 1,
"sc": "Thai"
},
"ti": {
"haswikt": 1,
"sc": "Ethi"
},
"tig": {
"sc": "Ethi"
},
"tiw": {
"sc": "Latn"
},
"tk": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tl": {
"haswikt": 1,
"sc": [
"Latn",
"Tglg"
]
},
"tmr": {
"sc": "Hebr"
},
"tn": {
"haswikt": 1
},
"to": {
"haswikt": 1
},
"tpi": {
"haswikt": 1
},
"tr": {
"g": [
"y",
"p"
],
"alt": 1,
"haswikt": 1
},
"ts": {
"haswikt": 1
},
"tt": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tts": {
"sc": "Thai"
},
"tw": {
"haswikt": 1
},
"udi": {
"sc": [
"Cyrl",
"Latn",
"Armn",
"Geor"
]
},
"ug": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ug-Arab"
},
"uga": {
"sc": "Ugar"
},
"uk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ulk": {
"sc": "Latn"
},
"ur": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ur-Arab"
},
"uz": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"vec": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"vi": {
"haswikt": 1
},
"vls": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"vo": {
"haswikt": 1
},
"wa": {
"haswikt": 1
},
"wbp": {
"sc": "Latn"
},
"wo": {
"haswikt": 1
},
"wuu": {
"sc": "Hani"
},
"xae": {
"sc": "Ital"
},
"xcr": {
"sc": "Cari"
},
"xfa": {
"sc": "Ital"
},
"xh": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"xlc": {
"sc": "Lyci"
},
"xld": {
"sc": "Lydi"
},
"xlu": {
"sc": "Xsux"
},
"xmf": {
"sc": "Geor"
},
"xno": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"xrr": {
"sc": "Ital"
},
"xst": {
"sc": "Ethi"
},
"xum": {
"sc": "Ital"
},
"xve": {
"sc": "Ital"
},
"xvo": {
"sc": "Ital"
},
"yi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"yo": {
"haswikt": 1
},
"yua": {
"g": [
"y",
"p"
],
"alt": 1
},
"yue": {
"sc": "Hani"
},
"za": {
"haswikt": 1,
"sc": "Latn"
},
"zh-classical": {
"sc": "Hani"
},
"zh-min-nan": {
"haswikt": 1
},
"zh-yue": {
"sc": "Hani"
},
"zu": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"zza": {
"g": [
"n",
"m",
"mn",
"n-p",
"m-p",
"p"
],
"wiktprefix": "diq"
}
},
"ziman": {
"ku": "kurmancî",
"en": "îngilîzî",
"tr": "tirkî",
"de": "almanî",
"pl": "polonî",
"ja": "japonî",
"fa": "farisî",
"sh": "sirboxirwatî",
"fr": "fransî",
"ka": "gurcî",
"it": "îtalî",
"fi": "fînî",
"ru": "rusî",
"es": "spanî",
"ar": "erebî",
"nl": "holendî",
"hi": "hindî",
"cs": "çekî",
"cmn": "mandarînî",
"pt": "portugalî",
"uk": "ûkraynî",
"hu": "mecarî",
"ko": "koreyî",
"be": "belarusî",
"la": "latînî",
"ckb": "soranî",
"sv": "swêdî",
"hy": "ermenî",
"bg": "bulgarî",
"ro": "romanyayî",
"da": "danmarkî",
"gl": "galîsî",
"el": "yûnanî",
"eo": "esperantoyî",
"tl": "tagalogî",
"fo": "ferî",
"vi": "viyetnamî",
"ca": "katalanî",
"is": "îzlendî",
"et": "estonî",
"az": "azerî",
"ceb": "sebwanoyî",
"mk": "makedonî",
"he": "îbranî",
"th": "tayî",
"lv": "latviyayî",
"sk": "slovakî",
"ms": "malezî",
"ga": "îrlendî",
"tg": "tacikî",
"grc": "yûnaniya kevn",
"sl": "slovenî",
"kk": "qazaxî",
"mi": "maorî",
"mn": "mongolî",
"sq": "albanî",
"uz": "ozbekî",
"lt": "lîtwanî",
"ur": "urdûyî",
"km": "ximêrî",
"id": "endonezyayî",
"cy": "weylsî",
"lo": "lawsî",
"bn": "bengalî",
"zza": "zazakî",
"ky": "kirgizî",
"yi": "yidîşî",
"gd": "gaelîka skotî",
"my": "burmayî",
"te": "telûgûyî",
"nb": "norweciya bokmålî",
"zh": "çînî",
"ps": "peştûyî",
"eu": "baskî",
"oc": "oksîtanî",
"sw": "swahîlî",
"ast": "astûrî",
"af": "afrîkansî",
"tk": "tirkmenî",
"nn": "norweciya nînorskî",
"yue": "kantonî",
"ug": "oygurî",
"rm": "romancî",
"sc": "sardînî",
"vo": "volapûkî",
"io": "îdoyî",
"ml": "malayalamî",
"mt": "maltayî",
"ba": "başkîrî",
"ta": "tamîlî",
"ang": "îngilîziya kevn",
"sa": "sanskrîtî",
"tt": "teterî",
"rup": "aromanî",
"nrf": "normandî",
"no": "norwecî",
"scn": "sicîlî",
"br": "bretonî",
"as": "asamî",
"bo": "tîbetî",
"nan": "minnanî",
"nv": "navajoyî",
"fy": "frîsî",
"pa": "puncabî",
"vec": "venîsî",
"fur": "friyolî",
"wa": "walonî",
"haw": "hawayî",
"lb": "luksembûrgî",
"am": "amharî",
"arz": "erebiya misrî",
"ia": "înterlîngua",
"kl": "kalalîsûtî",
"mr": "maratî",
"gv": "manksî",
"qu": "keçwayî",
"dsb": "sorbiya jêrîn",
"si": "kîngalî",
"kn": "kannadayî",
"sdh": "kurdiya başûrî",
"ota": "osmanî",
"gu": "gujaratî",
"ce": "çeçenî",
"jv": "javayî",
"pdt": "plodîşî",
"chr": "çerokî",
"nds": "nedersaksî",
"acw": "erebiya hîcazî",
"cu": "slaviya kevn",
"ne": "nepalî",
"kw": "kornî",
"hsb": "sorbiya jorîn",
"za": "zhuangî",
"os": "osetî",
"se": "samiya bakurî",
"dng": "dunganî",
"izh": "îngriyî",
"sco": "skotî",
"got": "gotî",
"or": "oriyayî",
"mnc": "mançûyî",
"crh": "teteriya krîmî",
"ltg": "latgalî",
"sah": "yakutî",
"ht": "haîtî",
"ary": "erebiya fasî",
"ha": "hawsayî",
"zu": "zuluyî",
"cv": "çuvaşî",
"yo": "yorubayî",
"ab": "abxazî",
"so": "somalî",
"frr": "frîsiya bakur",
"lad": "ladînoyî",
"an": "aragonî",
"ti": "tigrînî",
"aii": "aramiya nû ya asûrî",
"hak": "hakkayî",
"enm": "îngilîziya navîn",
"pi": "palî",
"navz": "navzimanî",
"ny": "çîçewayî",
"om": "oromoyî",
"nds-de": "almaniya jêrîn a almanî",
"lmo": "lombardî",
"csb": "kaşûbî",
"alt": "altayiya başûrî",
"arc": "aramî",
"bcl": "bîkoliya naverast",
"co": "korsîkayî",
"oj": "ojibweyî",
"pap": "papyamentoyî",
"rue": "rusînî",
"av": "avarî",
"txb": "toxarî B",
"xal": "kalmîkî",
"cop": "qiptî",
"mg": "malagasî",
"nap": "napolîtanî",
"gsw": "elmanîşî",
"dlm": "dalmatî",
"gn": "guwaranî",
"orv": "slaviya rojavayî ya kevn",
"tyv": "tuvanî",
"dv": "divehî",
"ady": "adîgeyî",
"bua": "buryatî",
"wuu": "wuyî",
"pam": "kapampanganî",
"myv": "erzayî",
"udm": "udmurtî",
"khb": "lueyî",
"rom": "romanî",
"su": "sundanî",
"ilo": "îlokanoyî",
"syc": "siryaniya klasîk",
"zdj": "şîngazîcayî",
"cdo": "min dongî",
"evn": "evenkî",
"kum": "kumikî",
"bal": "belûçî",
"lld": "ladînî",
"inh": "îngûşî",
"sd": "sindhî",
"lez": "lezgînî",
"fro": "fransiya kevn",
"non": "norsiya kevn",
"liv": "lîvonî",
"tet": "tetûmî",
"tpi": "tokpisinî",
"shn": "şanî",
"wo": "wolofî",
"mrw": "maranaoyî",
"gld": "nanayî",
"nah": "nahwatlî",
"ki": "kîkûyûyî",
"st": "sotoyiya başûr",
"mww": "hmongî",
"lij": "lîgûrî",
"rgn": "romagnolî",
"li": "lîmbûrgî",
"sm": "samoayî",
"sga": "îrlendiya kevn",
"ks": "keşmîrî",
"pms": "piedmontîsî",
"ccc": "çamicuroyî",
"hil": "hîlîgaynonî",
"xmf": "megrelî",
"hrx": "hunsrikî",
"rif": "tarifitî",
"krc": "karaçay-balkarî",
"war": "warayî",
"mwl": "mîrandî",
"syl": "sîlhetî",
"ee": "eweyî",
"ln": "lingalayî",
"pdc": "almaniya Pennsylvania",
"goh": "almaniya bilind a kevn",
"tly": "talişî",
"nci": "nahwatliya klasîk",
"ckt": "çukçî",
"ain": "aynuyî",
"uga": "ugarîtî",
"xh": "xosayî",
"sms": "samiya skoltî",
"kea": "kapverdî",
"ajp": "erebiya başûrê şamê",
"kjh": "xakasî",
"iu": "înuîtî",
"dz": "conxayî",
"srn": "sirananî",
"vro": "voroyî",
"mdf": "mokşayî",
"xcl": "ermeniya kevn",
"mhr": "mariya rojhilatî",
"cic": "çîkasawî",
"esu": "yupîkiya navendî",
"cjs": "şorî",
"wym": "wîlamowî",
"apw": "apaçiya rojava",
"mba": "higaononî",
"kaw": "javayiya kevn",
"ovd": "elfdalî",
"to": "tongî",
"ist": "îstriyotî",
"szl": "silesî",
"yua": "mayayiya yukatekî",
"tsg": "tausugî",
"zea": "zêlandî",
"bar": "bayerî",
"luy": "luhyayî",
"mzn": "mazenderanî",
"mhn": "mòçenoyî",
"kxd": "bruneyî",
"lzz": "lazî",
"kbd": "kabardî",
"afb": "erebiya kendavî",
"krl": "karelî",
"eve": "evenî",
"stq": "frîsiya saterlandî",
"ak": "akanî",
"tab": "tabasaranî",
"sat": "santalî",
"fj": "fîjî",
"sva": "svanî",
"egy": "misrî",
"ig": "îgboyî",
"hac": "hewramî",
"ay": "aymarayî",
"gur": "farefareyî",
"rw": "kînyarwandayî",
"twf": "taosî",
"koi": "komî-permyakî",
"lkt": "lakotayî",
"akl": "aklanon",
"yai": "yagnobî",
"ryu": "okînawanî",
"rhg": "rohingyayî",
"vep": "vepsî",
"smn": "samiya înarî",
"egl": "emilî",
"ty": "tahîtî",
"mdh": "maguindanaoyî",
"gag": "gagawzî",
"tzm": "temazîxtiya atlasa navendî",
"sgs": "samogîtî",
"ff": "fulahî",
"lou": "kreyoliya fransî ya Louisiana",
"mel": "melanauyiya navendî",
"niv": "nîvxî",
"aa": "afarî",
"gez": "etiyopiya klasîk",
"ext": "extremaduranî",
"frp": "frankoprovansî",
"kpv": "komî-zîriyî",
"udi": "udî",
"sma": "samiya başûr",
"nog": "nogayî",
"akk": "akadî",
"lbe": "lakî",
"min": "mînangkabawî",
"kg": "kîkongoyî",
"ban": "balînî",
"dlg": "dolganî",
"ace": "açehî",
"pox": "polabî",
"ppl": "pîpîlî",
"mnz": "monî",
"kaa": "karakalpakî",
"kab": "kabîlî",
"vot": "votî",
"luo": "luoyî",
"sn": "şonayî",
"swb": "komorî",
"roa-tar": "tarantinoyî",
"lg": "lugandayî",
"nds-nl": "saksoniya jêrîn ya holendî",
"dtp": "dusuniya navendî",
"shi": "taşelhitî",
"msk": "mansakayî",
"rap": "rapanuyî",
"mer": "meruyî",
"ik": "înupîakî",
"prg": "prusiya kevn",
"chk": "çûkî",
"kam": "kambayî",
"chy": "çeyenî",
"nod": "tayiya bakurî",
"akz": "alabamayî",
"xag": "aghwanî",
"ase": "zimanê destan ê emerîkî",
"nmn": "ǃxóõ",
"gun": "mbya guaraniyî",
"cr": "krî",
"yag": "yamanayî",
"otk": "tirkiya kevn",
"sid": "sidamoyî",
"tum": "tumbukayî",
"pjt": "pîttcantcatcarayî",
"sux": "sûmerî",
"bbl": "batsbî",
"jje": "jejuyî",
"bm": "bambarayî",
"frm": "fransiya navîn",
"tpn": "tupînambayî",
"pal": "farisiya navîn",
"bi": "bîslamayî",
"ksw": "s'gaw karenî",
"ssy": "sahoyî",
"bdr": "bajwayiya kendava rojava",
"sne": "bau bidayuh",
"sjd": "samiya kildînî",
"na": "nawrûyî",
"pag": "pangasînanî",
"abq": "abazayî",
"tn": "tswanayî",
"osx": "saksoniya kevn",
"aqc": "arçî",
"xsr": "şerpayî",
"kok": "konkanî",
"kln": "kalencînî",
"win": "wînnebagoyî",
"dar": "dargwayî",
"mfe": "kreyoliya morîtanî",
"iba": "îbanî",
"ae": "avestayî",
"mos": "moreyî",
"arn": "mapuçî",
"ill": "îranunî",
"mrj": "mariya rojavayî",
"cst": "ohloniya bakur",
"blt": "tay dam",
"apc": "erebiya bakûrê şamê",
"gan": "ganî",
"ii": "yiyiya siçuwayî",
"xto": "toxarî A",
"wmw": "mwanî",
"avd": "elwîrî-wîderî",
"pro": "oksîtaniya kevn",
"pcc": "buyî",
"jra": "jarayî",
"new": "newarî",
"unm": "unamî",
"mic": "mîkmakî",
"cjm": "çamiya rojhilatî",
"sdn": "galûrîsî",
"lus": "mizoyî",
"cjy": "jinî",
"tli": "tlingitî",
"bug": "bugî",
"pcd": "pîkardî",
"hop": "hopîyî",
"ofs": "frîsiya kevn",
"tft": "ternateyî",
"roa-opt": "portugaliya kevn",
"gil": "kîrîbatî",
"kac": "cingphoyî",
"csm": "sierra mîwokiya navendî",
"sdc": "sassarîsî",
"ood": "o'odhamî",
"hsn": "xiangî",
"ruo": "îstro-romanyayî",
"mbb": "bukidnon manoboyiya rojavayî",
"bho": "bojpûrî",
"ani": "andî",
"kgp": "kayingangî",
"ss": "swazî",
"mnp": "min beî",
"aho": "ahomî",
"moe": "montagnaîsî",
"naq": "namayî",
"mvi": "miyakoyî",
"duu": "drungî",
"lut": "luşûtsîdî",
"sce": "dongşîangî",
"bqi": "bextiyarî",
"mrc": "marîkopayî",
"gmh": "almaniya bilind a navîn",
"cim": "cimbrî",
"aer": "arrernteyiya rojavayî",
"agj": "argobbayî",
"ium": "îu mienî",
"tay": "atayalî",
"ale": "alêwîtî",
"str": "saanîçî",
"zku": "kaurnayî",
"pnt": "yûnaniya pontî",
"gmw-cfr": "frankoniya navendî",
"bxd": "pelayî",
"ems": "alutîqî",
"mtq": "muongî",
"hit": "hîtîtî",
"mak": "makasarî",
"bjn": "banjarî",
"nay": "ngarrindjerî",
"dak": "dakotayî",
"yrk": "nenetsiya tundra",
"pac": "pacohî",
"ch": "çamoroyî",
"vls": "flamiya rojava",
"acm": "erebiya iraqî",
"smj": "samiya lule",
"lmy": "lamboyayî",
"jam": "kreyoliya jameîkî",
"yka": "yakanî",
"gmw-ecg": "almaniya navendî ya rojhilat",
"xsv": "sûdovî",
"cdm": "çepangî",
"bdk": "buduxî",
"isd": "îsnagî",
"zgh": "amazîxiya fasî",
"css": "ohloniya başûr",
"kqr": "kîmaragangî",
"glk": "gîlekî",
"aeb": "erebiya tunusî",
"lki": "lekî",
"kdr": "karaîmî",
"kld": "gamîlaraayî",
"tdd": "tayî nüa",
"xqa": "karaxanîdî",
"nso": "sotoyiya bakur",
"crs": "kreyoliya seyşelî",
"mvv": "tagal murutî",
"kim": "tofalarî",
"swg": "swabî",
"pau": "palawî",
"wrh": "wiradhurî",
"cho": "çoktawî",
"tig": "tigreyî",
"ivv": "îvatanî",
"ddg": "fatalukuyî",
"rmf": "romaniya kalo fînî",
"pih": "pîtkarnî-norfolkî",
"phn": "fenîkî",
"ktu": "kitubayî",
"mwr": "marwarî",
"wbp": "warlpirî",
"zun": "zuniyî",
"crk": "kriya bejayî",
"ebk": "bontociya rojhilatî",
"nup": "nupeyî",
"yoi": "yonagunî",
"atj": "atîkamekwî",
"duo": "dupaningan agtayî",
"kca": "xantî",
"dta": "daurî",
"rar": "rarotongî",
"abe": "abenakî",
"aot": "atong (Hindistan)",
"osa": "osageyî",
"ami": "amîs",
"sea": "semaî",
"mh": "marşalî",
"mad": "madurayî",
"sei": "sêriyî",
"moh": "mohawkî",
"meu": "hîrî motuyî",
"pwn": "paîwanî",
"okm": "koreyiya navîn",
"xug": "kunigamî",
"amu": "amuzgoyî",
"ine-pro": "proto-hindûewropî",
"tzo": "tzotzîlî",
"ksc": "kalingayiya başûrî",
"sib": "sebopî",
"uum": "ûrûmî",
"kjj": "khinalugî",
"pis": "pijînî",
"peo": "farisiya kevn",
"sg": "sangoyî",
"tyz": "tàyî",
"kpy": "koryakî",
"ruq": "megleno-romanyayî",
"abs": "ambonezî-malezî",
"mas": "masayî",
"bdq": "bahnarî",
"mkz": "makasaeyî",
"mag": "magahî",
"oaa": "orokî",
"din": "dinkayî",
"zlw-slv": "slovînsî",
"olo": "livvî",
"mai": "maithilî",
"nhn": "nahwatliya navendî",
"nan-tws": "teoçewî",
"wuh": "wutunhuayî",
"tpw": "tûpiya kevn",
"gcf": "kreyoliya antîlleyî",
"knb": "lubuagan kalingayî",
"ket": "ketî",
"pua": "purepeçayî",
"xbr": "kamberayî",
"bor": "bororoyî",
"rmy": "romaniya vlaks",
"tao": "yamî",
"arq": "erebiya cezayirî",
"brh": "brahuiyî",
"chn": "çinook jargonî",
"cbk": "çavacanoyî",
"tvo": "tidoreyî",
"wbk": "waigalî",
"nio": "nganasanî",
"bsh": "kamkata-viriyî",
"roa-gal": "galoyî",
"krj": "kinaray-ayî",
"gmy": "yûnaniya mîkenî",
"ctd": "tedim çinî",
"ude": "udiheyî",
"gmw-rfr": "frankoniya rhine",
"tvl": "tuvalûyî",
"ibg": "îbanagî",
"ask": "aşkunî",
"ccp": "çakmayî",
"ts": "tsongayî",
"sje": "pite samî",
"esh": "iştihardî",
"quc": "k'içe'",
"ho": "hirî motuyî",
"hif": "fiji hindiyî",
"orh": "oroqenî",
"ett": "etrûskî",
"zai": "îsthmus zapotekî",
"bla": "blakfotî",
"zlw-opl": "poloniya kevn",
"ses": "sonxayî",
"lis": "lisuyî",
"kr": "kanuriyî",
"bbc": "bataktobayî",
"umb": "umbunduyî",
"rys": "yaeyamayî",
"arp": "arapaho",
"bol": "boleyî",
"com": "komançî",
"ifk": "tuwali ifugaoyî",
"ve": "vendayî",
"kri": "krioyî",
"aoz": "uab meto",
"yxg": "yagarayî",
"zom": "zouyî",
"ttt": "tatî",
"ams": "amamî-oşîmayiya başûrî",
"tts": "isanî",
"igl": "îgalayî",
"xda": "darkinjungî",
"fkv": "kvenî",
"chg": "çaxatayî",
"xwo": "oyratiya nivîsî",
"cab": "garîfunayî",
"atv": "altayiya bakurî",
"tiw": "tiwî",
"fit": "meänkiyelî",
"mnk": "mandinkayî",
"yox": "yoronî",
"tfn": "dena'inayî",
"hur": "halkomelemî",
"ryn": "amamî-oşimayiya bakurî",
"rop": "kriolî",
"ckv": "kavalanî",
"ulc": "ulçî",
"hts": "hadzayî",
"anq": "jarawa",
"dgr": "dogrîbî",
"myp": "pîrahayî",
"xfa": "faliskanî",
"gin": "hinukhî",
"pqm": "malecite-passamaquoddyî",
"mni": "manipurî",
"txg": "tangutî",
"trv": "tarokoyî",
"kha": "khasiyî",
"odt": "holendiya kevn",
"tby": "tabaruyî",
"drg": "rungusî",
"yur": "yurokî",
"neg": "negidalî",
"mga": "îrlendiya navîn",
"gor": "gorontaloyî",
"tcy": "tuluyî",
"oac": "oroçî",
"nia": "nîasî",
"skb": "saekî",
"bth": "biatah bidayuhî",
"nwy": "nottoway-meherrînî",
"chc": "katawbayî",
"bgc": "haryanvî",
"tih": "timugon murutî",
"itl": "îtelmenî",
"gml": "almaniya jêrîn a navîn",
"apj": "xîkarîyayî",
"yao": "yaoyî",
"okn": "oki-no-erabu",
"juc": "jurçenî",
"bnn": "bununî",
"nmb": "nambasiya mezin",
"cuk": "kunayî",
"uby": "ûbîkî",
"bpy": "manîpûriya bîşnûpriyayî",
"alr": "alutor",
"nys": "nyungayî",
"niq": "nandî",
"nnt": "nanticokeyî",
"shy": "şawî",
"xum": "umbriyî",
"kzg": "kikaiyî",
"blk": "pa'o karen",
"khi-kun": "ǃkungî",
"amw": "neo-aramiya rojavayî",
"kyh": "karokî",
"arw": "arawak",
"rkt": "kamtayî",
"ijc": "îzonî",
"jct": "krymçakî",
"xpq": "mohegan-pequotî",
"lnd": "lundayehî",
"xnb": "kanakanabuyî",
"niu": "nîwî",
"wls": "walîsî",
"axm": "ermeniya navîn",
"kap": "bezhtayî",
"ykg": "yukaghiriya bakurî",
"kky": "guugu yimidhirrî",
"hnj": "hmongiya hêşîn",
"dtr": "lotudî",
"kla": "klamatî-modokî",
"fax": "falayî",
"sjt": "ter samî",
"nxq": "naxî",
"oge": "gurciya kevn",
"apm": "çîrîkawayî",
"tzj": "tzutucîlî",
"tbl": "tbolî",
"inc-oas": "asamiya zû",
"hai": "haidayî",
"sou": "taiya başûrî",
"osc": "oskanî",
"shh": "şoşonî",
"xmz": "morî bawah",
"sju": "ume samî",
"xpr": "partî",
"gaa": "gayî",
"nch": "nahwatliya huastecaya navendî",
"bsk": "buruşaskiyî",
"bzj": "kreyoliya belizeyî",
"nhx": "nahwatliya mekayapan",
"ktb": "kambaatayî",
"xnn": "kankanayiya bakurî",
"pot": "potawatomî",
"agr": "aguarunayî",
"bsb": "bisayayiya bruneyî",
"cja": "çamiya rojavayî",
"nov": "noviyalî",
"kbp": "kabiyeyî",
"tkn": "toku-no-şîmayî",
"lzh": "çîniya klasîk",
"caa": "ç'orti'",
"paw": "pawnî",
"wbl": "waxiyî",
"rut": "rutulî",
"lud": "ludianî",
"gon": "gondiyî",
"lsd": "lîşana denî",
"pyu": "puyumayî",
"bej": "bejayî",
"sxr": "saaroayî",
"bsq": "bassayî",
"cmg": "mongoliya klasîk",
"mus": "krîkî",
"sas": "sasakî",
"mlm": "mulamî",
"aio": "aytonî",
"nuk": "nootkayî",
"aiw": "aarî",
"enq": "engayî",
"chl": "cahuillayî",
"huz": "hunzibî",
"grt": "garoyî",
"lic": "hlaiyî",
"fia": "nobiinî",
"osp": "spaniya kevn",
"amm": "amayî",
"ote": "otomiya mezquital",
"roa-leo": "leonîzî",
"dum": "holendiya navîn",
"awa": "awadhî",
"otw": "ottawayî",
"xbc": "baxterî",
"bem": "bembayî",
"ira-pro": "proto-îranî",
"dbl": "diyirbalî",
"mcm": "kristangî",
"ctu": "çolî",
"grk-mar": "yûnaniya mariupol",
"nst": "tangsayî",
"cak": "kaqçikelî",
"pov": "kreyoliya guinea-bissau",
"mbf": "malayiya baba",
"xsy": "saisiyatî",
"bno": "asî",
"lbj": "ladakhiyî",
"adt": "adnyamatanhayî",
"ttj": "tooroyî",
"apy": "apalayî",
"pim": "powhatanî",
"roa-brg": "bûrgûndî",
"szy": "sakizayayî",
"tkl": "tokelawî",
"wlm": "weylsiya navîn",
"gdo": "godoberî",
"ayn": "erebiya sanaanî",
"ksk": "kansayî",
"aht": "ahtnayî",
"btx": "batakiya karo",
"jdt": "tatiya cihû",
"cni": "aşanînkayî",
"skr": "seraikî",
"iir-pro": "proto-hindûîranî",
"yrl": "nhêngatûyî",
"sip": "sîkkîmî",
"ie": "înterlîngue",
"bin": "edoyî",
"sac": "foksî",
"wni": "komoriya ndzwanî",
"btq": "batekî",
"bkd": "binukid",
"gmq-scy": "skanî",
"rah": "rabhayî",
"ddo": "tsezî",
"tuw-sol": "solonî",
"gni": "gooniyandiyî",
"rmn": "romaniya balkanî",
"roa-poi": "poitevin-saintongeaisî",
"mqm": "marquesana başûr",
"tsu": "tsouyî",
"yol": "yolayî",
"fud": "futunî",
"umu": "munsî",
"wam": "massaçusettî",
"bsy": "sabah bisayayî",
"prn": "prasunî",
"clm": "klallamî",
"ssf": "thaoyî",
"jbo": "lojbanî",
"nus": "nuerî",
"mhx": "lhao voyî",
"hmc": "huişui hmongiya navendî",
"lrc": "luriya bakurî",
"tiy": "tirurayî",
"kkh": "khünî",
"nfr": "nafaanrayî",
"zpq": "zapotekiya zoogoço",
"suk": "sukumayî",
"rml": "romaniya baltîkî",
"ncj": "nahwatliya bakurê puebla",
"nhg": "nahwatliya tetelcingo",
"ars": "erebiya necdî",
"bh": "biharî",
"caq": "car nicobareseyî",
"wlc": "mwalî komorî",
"apd": "erebiya sudanî",
"mop": "mayayiya mopanî",
"yaq": "yaquî",
"pon": "ponpeyî",
"kwk": "kwak'walayî",
"wlo": "wolioyî",
"nnr": "narunggayî",
"agx": "aghulî",
"dty": "doteliyî",
"ybh": "yakkhayî",
"azz": "puebla nahuatliya çiyayî",
"yuy": "yuguriya rojhilat",
"tqw": "tonkawayî",
"hss": "harsusiyî",
"mqu": "mandarî",
"maz": "mazahuayiya navendî",
"ife": "îfèî",
"kmk": "limos kalingayî",
"shs": "şuswapî",
"hid": "hidatsayî",
"brg": "baureyî",
"wrr": "wardamanî",
"kis": "kîsî",
"acy": "erebiya qibrisî",
"tin": "tindî",
"gmq-osw": "swêdiya kevn",
"yuf": "havasupaî-walapaî-yavapaî",
"xdk": "darûkî",
"wau": "waujayî",
"ljp": "lampungî",
"alu": "areareyî",
"ach": "açolî",
"bkm": "komiya kamerûnî",
"ngn": "ngwoyî",
"bsa": "abînomnî",
"der": "deoriyî",
"shp": "şipibo-coniboyî",
"ycl": "lolopoyî",
"qua": "kuapawî",
"gmq-pro": "proto-norsî",
"nnv": "nukunuyî",
"zko": "kottî",
"khv": "khvarşiyî",
"nij": "ngajuyî",
"yog": "yogadî",
"raw": "rawangî",
"mfy": "mayoyî",
"nd": "ndebeliya bakurî",
"oko": "koreyiya kevn",
"fon": "fonî",
"abx": "abaknonî",
"rad": "radeyî",
"klj": "khalajî",
"tru": "turoyo",
"nag": "naga pidginî",
"crl": "kriya rojhilat ya bakurî",
"zlw-ocs": "çekiya kevn",
"aau": "abauyî",
"mrq": "marquesana bakur",
"sia": "samiya akkalayî",
"mjg-mgl": "mongghulî",
"car": "galîbî karîbî",
"roa-tou": "torangî",
"rel": "rendilleyî",
"oma": "omaha-poncayî",
"rmq": "kaloyî",
"rmo": "romaniya sinte",
"lep": "lepçayî",
"guw": "gunî",
"knj": "akatekî",
"slr": "salarî",
"sem-srb": "erebiya başûrî ya kevn",
"pad": "paumarî",
"yap": "yapî",
"xul": "ngunawalî",
"kzj": "kadazaniya beravî",
"fan": "fangî (Bantu)",
"brx": "bodoyî (Hindistan)",
"ane": "xârâcùù",
"cro": "crowî",
"ram": "kanelayî",
"tea": "temiarî",
"cnk": "khumi çinî",
"tkr": "tsakhurî",
"ulk": "meryamî",
"tzh": "tzeltalî",
"avk": "kotavayî",
"mjg-mgr": "mangghuerî",
"xav": "xavanteyî",
"kmv": "kreyoliya fransî ya karipúna",
"aas": "aasayî",
"guz": "gusiiyî",
"erk": "efateyiya başûr",
"ngh": "nlûyî",
"cel-gau": "gaulisî",
"hz": "hereroyî",
"alq": "algonquinî",
"cay": "cayugayî",
"inz": "înesenyoyî",
"sgr": "sangisarî",
"iro-min": "mingoyî",
"dru": "rukaiyî",
"tta": "tuteloyî",
"lui": "luiseñoyî",
"mgm": "mambayî",
"xpm": "pumpokolî",
"kyj": "karaoyî",
"ksd": "tolaiyî",
"pkc": "baekceyî",
"pcm": "pidgîniya nîjeryayî",
"kjg": "khmuyî",
"cow": "cowlitzî",
"cyo": "cuyunonî",
"msn": "vuresî",
"jhi": "jehaiyî",
"map-bms": "banyumasanî",
"fla": "montana salişî",
"skd": "sierra miwokiya başûrî",
"xpu": "punîkî",
"tsd": "tsakonî",
"gue": "gurindjiyî",
"ksi": "krisayî",
"pni": "aohengî",
"par": "panamintî",
"ulb": "ulukwumî",
"miq": "miskitoyî",
"mcb": "maçiguengayî",
"kls": "kalaşayî",
"sdo": "bukar-sadung bidayuhî",
"nr": "ndebeliya başûrî",
"mch": "maquiritarî",
"msb": "masbatenyoyî",
"tsi": "tsimşî",
"gmq-gut": "gutnişî",
"tus": "tuskarorayî",
"lmw": "miwokiya golî",
"srs": "sarceeyî",
"zag": "zaghawayî",
"klb": "kiliwayî",
"kmc": "kamiya başûrî",
"beg": "belaîtî",
"inc-ash": "prakrîtiya aşokanît",
"kj": "kwanyamayî",
"ng": "ndongayî",
"irk": "îraqwî",
"grk-ita": "grîkoyî",
"aaq": "penobskotî",
"kek": "q'eqçiyî",
"ojb": "ojibweyiya bakurî",
"txu": "kayapó",
"one": "oneidayî",
"ktv": "katuyiya rojhilatî",
"nhe": "nahwatliya huastekaya rojhilatî",
"lwl": "lawayiya rojhilatî",
"lua": "luba-kasayî",
"bjb": "barngarla",
"acu": "açuarî",
"cof": "tsafikiyî",
"gwi": "gwiçinî",
"urk": "urak lawoi'",
"tar": "tarahumarayiya navendî",
"kva": "bagvalalî",
"hni": "haniyî",
"sek": "sekanî",
"cpg": "yûnaniya kapadokyayî",
"wmb": "wambayayî",
"xld": "lîdyayî",
"amn": "amanabî",
"haa": "hänî",
"cdy": "çadongî",
"akr": "arakî",
"kju": "kaşayayî",
"ada": "adangmeyî",
"alh": "alawayî",
"gdq": "mehriyî",
"tmo": "temoqî",
"kus": "kusaalî",
"dwu": "dhuwalî",
"pak": "parakanã",
"agg": "angorî",
"wao": "wappoyî",
"aji": "ajië",
"cas": "tsimaneyî",
"byq": "basayî",
"xls": "lusîtanî",
"gcr": "kreyoliya guwiyanîsî",
"apk": "apaçiya deştan",
"kut": "kutenaiyî",
"gmw-jdt": "danmarkiya jersey",
"hus": "wastekî",
"rej": "rejangî",
"dog": "dogonî",
"roa-lor": "lorrainî",
"emb": "embalohî",
"tnq": "taînoyî",
"crj": "kriya rojhilat ya başûrî",
"nuj": "nyolî (Uganda)",
"gmq-oda": "danmarkiya kevn",
"akv": "axvaxî",
"doi": "dogriyî",
"trp": "kokborokî",
"geh": "hutterisçî",
"gul": "gullahî",
"coo": "comoxî",
"ije": "biseniyî",
"itk": "îtaliya cihû",
"rmc": "romaniya karpatyayî",
"gmw-gts": "gottscheerîşî",
"ttg": "tutongî",
"ngu": "nahwatliya guerrero",
"pse": "malayiya navendî",
"onw": "nubiya kevn",
"qui": "kuwileytî",
"giw": "gelaoyiya spî",
"ojw": "ojibweyiya rojavayî",
"gay": "gayoyî",
"rmw": "romaniya welşî",
"nhw": "nahwatliya huastecaya rojavayî",
"xkl": "bakungî",
"loz": "loziyî",
"yij": "yindcîbarndî",
"kry": "krytsî",
"ple": "palueyî",
"tht": "tahltanî",
"mkq": "bay miwokî",
"sks": "maiayî",
"mhe": "besisî",
"mrv": "mangarevanî",
"nrm": "naromî",
"chp": "çîpewî",
"bll": "biloxî",
"knx": "kendayanî",
"khw": "khowarî",
"dua": "diwalayî",
"roa-cha": "şampanyî",
"gug": "guaraniyiya paraguayanî",
"dsn": "dusnerî",
"tmh": "tuaregî",
"pmf": "pamonayî",
"smq": "samoyî",
"tal": "talî",
"yuc": "yuçî",
"mqy": "manggarayî",
"bts": "batakiya simalungun",
"tos": "totonakiya çiyayî",
"oia": "oyratayî",
"blz": "balantak",
"bjz": "baruga",
"kai": "karekareyî",
"zav": "zapotekiya yatzaçî",
"okd": "okodiayî",
"ndd": "nde-nsele-nta",
"omc": "moçikayî",
"bto": "iriga bikolanoyî",
"okr": "kirikeyî",
"top": "totonakiya papantla",
"xer": "xerenteyî",
"xlc": "lîkî",
"mid": "mandaicî",
"suy": "suyayî",
"chz": "ozumacin çinantecî",
"pah": "tenharimî",
"til": "tillamookî",
"afn": "defakayî",
"guc": "wayuuyî",
"ojp": "japoniya kevn",
"lif": "limbuyî",
"efi": "efikî",
"mek": "mêkêoyî",
"ayl": "erebiya lîbyayî",
"bnt-phu": "phuthî",
"kne": "kankanaeyî",
"ktn": "karitiyanayî",
"abl": "abûngî",
"rtm": "rotumanî",
"wyb": "ngiyambaayî",
"crg": "mîçîfî",
"kxu": "kuiyî (Hindistan)",
"cts": "bicolanoyiya northern catanduanes",
"dje": "zarmayî",
"plw": "palawanoyî",
"xlu": "luwiyanî",
"koy": "koyukonî",
"vmw": "makhuwayî",
"vmf": "frankiya rojhilatî",
"bru": "bruyiya rojhilatî",
"kmb": "kimbunduyî",
"xsa": "sabaeanî",
"tbk": "tagbanwayiya kalamî",
"kzt": "tambunan dusunî",
"xng": "mongoliya navîn",
"acn": "açangî",
"bwx": "bu-nao bunuyî",
"cwg": "çewongî",
"vmb": "mbabaramî",
"ijs": "îjoyiya başûr-rojhilat",
"asb": "assînîboyî",
"nzd": "nzadî",
"itv": "îtawitî",
"lvk": "lavukaleveyî",
"bde": "badeyî",
"tbc": "takyayî",
"aby": "aneme wakeyî",
"urb": "urubú-kaaporî",
"coh": "çiçonyi-çidzihana-çikauma",
"dbj": "ida'anî",
"smk": "bolinaoyî",
"lbx": "lawanganî",
"kfr": "kaççiyî",
"hlt": "nga layî",
"agm": "angaatahayî",
"xpg": "firîgî",
"smr": "simeulueyî",
"vma": "martuthunirayî",
"gub": "guajajarayî",
"chf": "tabasco çontalî",
"mqs": "makyaniya rojava",
"iby": "îbaniyî",
"cng": "qiangiya bakurî",
"jac": "jakaltekî",
"trw": "torwalî",
"lti": "lêtîyî",
"scl": "şinayî",
"gwr": "gwereyî",
"dgc": "casiguran dumagat agtayî",
"btm": "batakiya mandaîlîng",
"puj": "punan tubuyî",
"oka": "okanaganî",
"gek": "gerkayî",
"mat": "matlatzincayî",
"kio": "kiowayî",
"cku": "koasatî",
"kms": "kamasauyî",
"xaa": "erebiya endulusî",
"gut": "maleku jaikayî",
"aou": "a'ou",
"elx": "îlamî",
"peh": "bonanî",
"pgl": "îrlendiya prîmîtîv",
"myx": "masabayî",
"spp": "supyireyî",
"xnt": "narragansettî",
"cad": "caddoyî",
"aap": "pará arárayî",
"rme": "angloromanî",
"ura": "urarinayî",
"mpm": "mixteciya yosondúa",
"poe": "popolocayiya san juan atzingo",
"sgd": "surigaononî",
"kvh": "komodoyî",
"xtc": "kaduglî",
"tjg": "tunjungî",
"azg": "san pedro amuzgos amuzgo",
"uun": "pazehî",
"udl": "uldemeyî",
"skp": "sekapanî",
"isi": "nkem-nkumî",
"wrz": "warrayî",
"its": "îsekiriyî",
"tgb": "tobilungî",
"bdy": "bandjalangî",
"ssb": "samayiya başûrî",
"hnn": "hanunooyî",
"enn": "engenniyî",
"srm": "saramakkanî",
"dmr": "damariya rojhilat",
"djo": "jangkangî",
"kij": "kîlîvîlayî",
"rob": "tae'",
"sad": "sandaweyî",
"har": "harariyî",
"hov": "hovonganî",
"pos": "popolucayiya sayula",
"sit-bok": "bokarî",
"kns": "kensiuyî",
"mbd": "dibabawon manoboyî",
"asu": "tocantins asurînî",
"sjw": "şawneeyî",
"btw": "butuanonî",
"jut": "jutişî",
"bgs": "tagabawayî",
"xsb": "sambalî",
"mkf": "miyayî",
"mrn": "çeke holoyî",
"kui": "kuikúroyî",
"hup": "hupayî",
"abm": "abanyomî",
"ukk": "muak sa-aakî",
"lbk": "bontociya navendî",
"dbp": "duwaiyî",
"jup": "hupdëî",
"cce": "çopiyî",
"muy": "muyangî",
"agt": "agtayiya kagayanî ya navendî",
"sxn": "sangirî",
"brc": "kreyoliya holendî ya berbiceyî",
"yux": "yukaghiriya başûrî",
"zpv": "zapotekiya çiçicapan",
"teo": "atesoyî",
"dif": "dieriyî",
"maq": "mazateciya çiquihuitlan",
"sgh": "şughnî",
"gnc": "guwançî",
"ega": "egayî",
"kbh": "camsayî",
"bni": "bangî",
"tdj": "tajioyî",
"ngi": "ngizimî",
"bue": "beothukî",
"pok": "pokangayî",
"myw": "muyuwî",
"bkn": "bukîtan",
"dyu": "dyulayî",
"asn": "xingú asuriní",
"cme": "cermayî",
"tna": "takanayî",
"mia": "miamî",
"epi": "epieyî",
"mcq": "eseyî",
"kyu": "kayahiya rojavayî",
"sai-pyk": "pykobjê",
"zaa": "zapotekiya sierra de juarez",
"dhv": "dehûyî",
"bxk": "bukusuyî",
"nuz": "nahwatliya tlamacazapa",
"trm": "tregamî",
"cps": "capiznonî",
"slm": "pangutaran samayî",
"cia": "cia-ciayî",
"abv": "erebiya bahranî",
"lod": "berawanî",
"mhj": "mogholî",
"mnv": "rennelleseyî",
"gir": "gelaoyiya sor",
"bfq": "badagayî",
"wyi": "woiwurrungî",
"eya": "eyakî",
"awk": "awabakal",
"zoc": "zoqueyiya copainala",
"smw": "sumbawayî",
"ank": "goemayî",
"mbl": "maxakalî",
"tbd": "kakî aeyî",
"nez": "nez perceyî",
"crn": "corayî",
"sai-tap": "tapayunayî",
"see": "senekayî",
"huv": "huaveyî",
"apn": "apinayé",
"mfa": "malayiya pattani",
"crx": "karriyerî",
"mam": "mamî",
"ibl": "îbaloiyî",
"cac": "çujî",
"kdt": "kuyî",
"ocu": "tlahuicayî",
"hvn": "sabuyî",
"cjp": "cabecarî",
"xbi": "kombioyî",
"xra": "krahô",
"blc": "bella koola",
"peq": "pomoyiya başûrî",
"bpz": "bilbayî",
"mzp": "movimayî",
"lhu": "lahuyî",
"kge": "komeringî",
"tue": "tuyukayî",
"taa": "tananaya jêrîn",
"vkp": "kreyoliya portugalî ya korlai",
"cmo": "mnongiya navendî",
"mov": "mojaveyî",
"kkz": "kaskayî",
"zro": "zaparoyî",
"mnb": "munayî",
"mwf": "murînpatayî",
"zkg": "goguryeoyî",
"kiw": "kiwaiyiya bakur-rojhilat",
"ebr": "ebrieyî",
"pmt": "tuamotuanî",
"roa-ang": "angevinî",
"ssg": "seimatî",
"hch": "huiçolî",
"bew": "betawî",
"ycn": "yucunayî",
"mbr": "nukak makuyî",
"apq": "a-pucikwar",
"nym": "nyamwezî",
"tbi": "gaamî",
"obi": "obispeñoyî",
"pri": "paicî",
"lu": "luba-katangayî",
"kcg": "tyapî",
"yui": "yurutî",
"kvm": "kendemî",
"djk": "aukanî",
"rir": "ribunî",
"lrg": "laragiayî",
"wnw": "wintuyî",
"woe": "woleayî",
"ljl": "li’oyî",
"kic": "kîkapûyî",
"ndh": "ndalî",
"hdy": "hadiyyayî",
"cbs": "caşinahuayî",
"kys": "baram kayanî",
"nxg": "ngadhayî",
"hix": "hixkaryanayî",
"mpe": "majangî",
"pwm": "molbogî",
"nyn": "nyankoleyî",
"ilb": "îlayî",
"qya": "qiwenyayî",
"wrk": "garawayî",
"def": "dezfuliyî",
"oym": "wayampî",
"rkb": "rikbaktsayî",
"sai-msk": "masakarayî",
"itz": "îtzayî",
"sgc": "kipsigisî",
"ppi": "paipaî",
"tji": "tujiayiya bakurî",
"yak": "yakîmayî",
"gvp": "para gaviãoyî",
"jmc": "maçameyî",
"bfs": "bayiya başûrî",
"ncb": "nicobariya navendî",
"kvb": "kubuyî",
"veo": "venturenyoyî",
"poi": "popolucayiya highland",
"bny": "bintuluyî",
"bhp": "bîmayî",
"rim": "nyaturuyî",
"sog": "sogdî",
"rth": "ratahanî",
"mse": "museyî",
"ncl": "nahwatliya mîçoakanê",
"acp": "akîpayiya rojhilatî",
"pao": "paiuteyiya bakurî",
"iyx": "yaayî",
"zhx-tai": "taişanîzî",
"coe": "koreguajeyî",
"ser": "serranoyî",
"nko": "nkonyayî",
"mgq": "malilayî",
"ppo": "folopayî",
"alc": "kawésqar",
"abg": "abagayî",
"wro": "wororayî",
"kre": "panarayî",
"tuv": "turkanayî",
"dav": "taitayî",
"ppk": "umayî",
"roa-oca": "katalaniya kevn",
"tso-lya": "çolyanî",
"myu": "mundurukú",
"dcc": "deccaniyî",
"lsi": "laşiyî",
"ugo": "gongî",
"nab": "nambikwarayî",
"lil": "lillooetî",
"gqi": "guiqiongî",
"gah": "alekanoyî",
"ncz": "natçezî",
"xom": "komoyî",
"prk": "paraukî",
"bea": "beaverî",
"sky": "sikayanayî",
"pui": "puinaveyî",
"kgg": "kusundayî",
"lml": "ragayî",
"ore": "orejónî",
"vbb": "babariya başûr-rojhilat",
"mbp": "wiwayî",
"bft": "baltî",
"lgg": "lugbarayî",
"mxd": "modangî",
"mir": "isthmus mixeyî",
"enf": "forest enetsî",
"pkp": "pukapukanî",
"kzi": "kelabît bariyoyî",
"wba": "waraoyî",
"xeg": "ǁxegwî",
"cpa": "palantla çinantecî",
"men": "mendeyî",
"btu": "batuyî",
"psy": "piscatawayî",
"xhu": "hurî",
"for": "foreyî",
"deg": "degemayî",
"sai-oto": "otomacoyî",
"nhv": "nahwatliya temascaltepec",
"gme-cgo": "gothîkiya krimî",
"kyi": "kîputî",
"olt": "lîtwanyayiya kevn",
"alp": "aluneyî",
"nbb": "ndoeyî",
"uvl": "loteyî",
"pab": "parecî",
"nbh": "ngamoyî",
"dra-okn": "kannadayiya kevn",
"aak": "ankaveyî",
"wiv": "vîtûyî",
"kuz": "kunzayî",
"atb": "zaiwa",
"aca": "açaguwayî",
"zmr": "maranungkuyî",
"xke": "kerehoyî",
"isk": "îşkaşimiyî",
"dig": "digoyî",
"agn": "agutaynenî",
"bji": "burjî",
"klw": "linduyî",
"bwi": "baniwayî",
"ygr": "yagariayî",
"mez": "menomineeyî",
"kag": "kajamanî",
"mey": "hassaniyayî",
"umo": "umotinayî",
"yii": "yidinyî",
"twy": "tawoyanî",
"ktw": "katoyî",
"ari": "arîkara",
"ysr": "sirenikî",
"adz": "adzerayî",
"spo": "spokanî",
"cgg": "rukîgayî",
"wuv": "wuvulu-auayî",
"etu": "ejaghamî",
"jmd": "yamdenayî",
"mhy": "ma'anyanî",
"tae": "taryanayî",
"kzr": "karangî",
"zhn": "nong zhuangî",
"ess": "yupikiya sîberyaya navendî",
"teg": "teke-tegeyî",
"cqd": "çuanqiandian cluster miaoyî",
"sri": "sirianoyî",
"dag": "dagbaniyî",
"gae": "warekenayî",
"lre": "laurentî",
"tbt": "kitemboyî",
"pgn": "paelignianî",
"onb": "lingaoyî",
"pth": "pataxó hã-ha",
"vmg": "minigirî",
"yah": "yazghulamî",
"kxn": "kanowitî",
"kde": "makondeyî",
"api": "apiaká",
"och": "çîniya kevn",
"pre": "prînsîpenî",
"mgr": "mambwe-lunguyî",
"stp": "tepehuaniya başûr-rojhilat",
"kak": "kayapa kallahanî",
"myg": "mantayî",
"kee": "keresiya rojhilatî",
"mfh": "matalî",
"bkr": "bakumpaî",
"alw": "alaba",
"pml": "sabirî",
"rag": "logûlî",
"mei": "midobî",
"mjy": "mahicanî",
"blm": "belî",
"nhu": "nooneyî",
"kuf": "katuyiya rojavayî",
"cux": "tepeuxila cuicatecî",
"nyy": "nyakyusayî",
"mtf": "murikî (Gîneya nû)",
"mrt": "margî",
"mpj": "martu wangkayî",
"tlh": "klîngonî",
"enh": "tundra enetsî",
"sai-crd": "coroado purî",
"umi": "ukitî",
"tcb": "tanakrosî",
"kpt": "karatayî",
"xvn": "vandalî",
"xsm": "kasemî",
"ltc": "çîniya navîn",
"yuz": "yuracareyî",
"bhw": "biyakî",
"sqt": "soqotrî",
"dny": "deniyî",
"lcp": "lawayiya rojavayî",
"tpx": "akatepekiya me'phaayî",
"ake": "akawaîo",
"jeb": "jeberoyî",
"gup": "gunwingguyî",
"xeb": "eblaiteyî",
"zkt": "xîtanî",
"lex": "luangî",
"mnq": "minriqî",
"njm": "angamî",
"sge": "segayî",
"hoi": "holîkaçukî",
"tee": "huehuetla tepehuayî",
"bil": "bileyî",
"gej": "genî",
"wim": "wik-mungkanî",
"oyd": "oydayî",
"gvj": "guajayî",
"blw": "balangao",
"lgt": "pahîyî",
"sai-cac": "cacanî",
"xok": "xoklengî",
"knv": "taboyî",
"slc": "salibayî (Kolombiya)",
"noz": "nayî",
"bnq": "bantîkî",
"ykm": "yakamulî",
"dym": "yanda dogonî",
"cri": "sãotomenseyî",
"shu": "erebiya çadî",
"tcs": "creoleyiya torres strait",
"gmw-stm": "sathmar swabî",
"rbl": "miraya bikolî",
"mbn": "macaguanî",
"mgu": "mailuyî",
"pib": "yineyî",
"yku": "kuamasî",
"gva": "kaskihayî",
"sai-yur": "yurumanguî",
"clw": "çulymî",
"bln": "bîkolanoyiya başûr katanduanî",
"mxi": "mozarabîkî",
"sob": "sobeyî",
"agd": "agarabiyî",
"bxe": "ongotayî",
"ito": "îtonamayî",
"mmq": "aisî",
"gnd": "zulgo-gemzekî",
"wig": "wik-ngathanayî",
"dto": "tommo soyî",
"suz": "sunwarî",
"rol": "romblomanonî",
"llu": "lawî",
"jaa": "jamamadiyî",
"bns": "bundelî",
"wal": "wolayttayî",
"oui": "oyguriya kevn",
"bjt": "balanta-ganjayî",
"alk": "alakî",
"nhr": "naroyî",
"gmw-vog": "almaniya volga",
"nsn": "nehanî",
"kqe": "kalaganî",
"end": "endeyî",
"jui": "ngadcurî",
"hay": "hayayî",
"svb": "ulau-suainî",
"saz": "sauraştrayî",
"mep": "miriwungî",
"ert": "eritaiyî",
"yen": "yendangî",
"mxb": "mixteciya tezoatlan",
"pmw": "plains miwokî",
"fak": "fangî (Beboid)",
"sai-bet": "betoî",
"arl": "arabela",
"cks": "tayoyî",
"nrn": "nornî",
"kao": "kassonkeyî",
"blb": "bilua",
"wab": "wabî",
"kwd": "kwaioyî",
"abr": "abronî",
"wbi": "wanjî",
"cso": "soçiapam çinantecî",
"kmo": "kwomayî",
"ddw": "dawera-daweloorî",
"zae": "zapotekiya yareni",
"rac": "rasawayî",
"bra": "brajî",
"naz": "nahwatliya koatepek",
"qwm": "kipçakî",
"ayz": "maybratî",
"amp": "alamblakî",
"sit-liz": "lizuyî",
"jum": "jumjumî",
"tub": "tübatulabalî",
"hih": "pamosuyî",
"kbc": "kadiweuyî",
"tun": "tunikayî",
"cod": "cocamayî",
"abt": "ambulasî",
"ame": "yaneşa'",
"xed": "hdî",
"yrb": "yarebayî",
"mbc": "macuşî",
"sai-mal": "malalî",
"khs": "kasuayî",
"soz": "temî",
"mva": "manamî",
"bcu": "bîlîwayî",
"zpo": "zapotekiya amatlan",
"tfr": "teribeyî",
"xgf": "gabrielino-fernandeñoyî",
"aah": "abu' arapeşî",
"adn": "adangî",
"btn": "ratagnonî",
"gby": "gbariyî",
"sxb": "subayî",
"onn": "onobasuluyî",
"aih": "aî-çamî",
"djm": "jamsayî",
"wun": "bunguyî",
"bhv": "bahauyî",
"ahk": "akha",
"tow": "jemezî",
"duw": "dusun wituyî",
"alj": "alanganî",
"sbk": "safwayî",
"kho": "xotanî",
"ndj": "ndambayî",
"mmh": "mehinakuyî",
"ttw": "long watî",
"sai-cat": "catacaoyî",
"kck": "kalangayî",
"yav": "yangbenî",
"heh": "heheyî",
"szw": "sawayî",
"phk": "phakeyî",
"aoa": "angolar",
"xur": "urartûyî",
"txn": "taranganiya rojava",
"piv": "pilenî",
"xta": "mixtekiya alcozauca",
"ute": "uteyî",
"tad": "tauseyî",
"yug": "yugî",
"zum": "kumzarî",
"nkr": "nukuoroyî",
"xaw": "kawaiisuyî",
"mpa": "mpotoyî",
"zng": "mangî",
"gad": "gaddangî",
"drn": "damariya rojava",
"oca": "ocainayî",
"mcg": "mapoyoyî",
"gbm": "garhwaliyî",
"sbh": "sorî-harenganî",
"kew": "kewayî",
"mki": "dhatkî",
"obt": "bretoniya kevn",
"alo": "larike-wakasihu",
"drl": "baagandjiyî",
"tog": "tongayî (Malawî)",
"tdl": "surî",
"met": "matoyî",
"cle": "lealao çinantecî",
"gmq-jmk": "jamtîşî",
"gvf": "golinî",
"tlc": "totonakiya misantla",
"bfg": "busang kayanî",
"kvc": "kovêyî",
"kay": "kamayurayî",
"hei": "heiltsukî",
"urt": "uratî",
"nll": "nihalî",
"bdp": "bendeyî",
"ybe": "yuguriya rojavayî",
"nbr": "numanayî",
"mht": "mandahuacayî",
"wap": "wapişanayî",
"dzg": "dazagayî",
"wrg": "warunguyî",
"tiv": "tivî",
"tox": "tobî",
"kvo": "dobelî",
"ubl": "buhi'non bikolî",
"aja": "ajayî",
"toi": "tongayî (Zambiya)",
"sus": "susuyî",
"nhm": "nahwatliya morelos",
"mcf": "matsesî",
"lew": "ledo kailiyî",
"ckx": "cakayî",
"bxg": "bangalayî",
"nem": "nemî",
"mgo": "meta'",
"eno": "engganoyî",
"crd": "coeur d'aleneyî",
"bdb": "basapî",
"cui": "cuibayî",
"emx": "erromintxelayî",
"bsw": "baysoyî",
"piw": "pimbweyî",
"nim": "nilambayî",
"mzs": "makanîzî",
"njo": "aoyî",
"duk": "uyajitayayî",
"old": "moçî",
"sam": "aramiya samarî",
"mlq": "malinkî",
"awd-kaw": "kawişanayî",
"mro": "mruyî",
"sjm": "mapunî",
"bot": "bongoyî",
"lay": "lama baiyî",
"aif": "agî",
"bks": "masbate sorsogon",
"ibd": "îwaidjayî",
"dob": "dobûyî",
"noa": "woun meuyî",
"too": "totonakiya xicotepec de juarez",
"duv": "duvleyî",
"bqo": "baloyî",
"mna": "mbulayî",
"pbh": "panareyî",
"cav": "cavineñayî",
"scg": "sanggauyî",
"con": "cofanî",
"ubr": "ubirî",
"ata": "pele-ata",
"ngj": "ngieyî",
"ers": "ersuyî",
"tro": "taraoyî",
"yki": "yokeyî",
"tya": "tauyayî",
"byk": "biaoyî",
"tuq": "tedagayî",
"xrn": "arinî",
"mhq": "mandanî",
"ktx": "kaxarariyî",
"beu": "blagarî",
"mtv": "asaro'oyî",
"zca": "zapotekiya coatecas altas",
"abd": "manîdeyî",
"imn": "îmondayî",
"ofo": "ofoyî",
"avv": "avá-kanoeîro",
"ree": "rejang kayanî",
"klr": "khalingî",
"dby": "dibiyasoyî",
"bzd": "bribriyî",
"iwm": "îwamî",
"bvk": "bukatî",
"mgw": "matumbî",
"aus-wul": "wulguruyî",
"mto": "totontepec mixeyî",
"inc-ogu": "gujaratiya kevn",
"kpm": "kohoyî",
"bgz": "banggaî",
"klg": "tagakaulu kalaganî",
"xab": "sambeyî",
"pnz": "panayî (Afrîkaya navendî)",
"snl": "sangilî",
"aoc": "pemon",
"cul": "culinayî",
"nnq": "ngindoyî",
"cji": "çamalalî",
"urh": "urhoboyî",
"hoc": "hoyî",
"yok-nvy": "yokutiya geliyê bakurî",
"nbp": "nnamî",
"qyp": "kuiripî",
"ski": "sikayî",
"whk": "wahau kenyahî",
"fos": "sirayayî",
"paa-nun": "nungonî",
"mtd": "mualangî",
"mrh": "mara çinî",
"xeu": "keoru-ahiayî",
"ass": "îpulo",
"ing": "deg xinagî",
"ajg": "adjayî",
"kjl": "khamî",
"wed": "wedauyî",
"mxz": "maselayiya navendî",
"miw": "akoyeyî",
"lmc": "limilnganî",
"bag": "tukî",
"bva": "bareinî",
"yal": "yalunkayî",
"asa": "pare",
"sai-col": "colanî",
"kdd": "yankunytjatjarayî",
"pne": "penaniya rojavayî",
"hna": "minayî",
"ayp": "erebiya mezopotamyayî ya bakur",
"can": "çambriyî",
"yey": "yeyî",
"szc": "semaq beriyî",
"byd": "benyadu'",
"yok-svy": "yokutiya geliyê başûrî",
"xet": "setayî",
"aro": "araona",
"ntw": "nottowayî",
"szv": "isubuyî",
"crw": "çrauyî",
"kyz": "kayabiyî",
"asl": "asîluluyî",
"poz-abi": "abay-sembwakî",
"tsx": "mubamî",
"ify": "keley-i kallahanî",
"srv": "waray sorsogonî",
"ong": "oloyî",
"mvm": "muyayî",
"lam": "lambayî",
"icl": "zimanê destan ê îzlandê",
"xww": "wemba-wembayî",
"ixl": "îxilî",
"ape": "bukiyîpî",
"rui": "rufijî",
"mhl": "mauwakeyî",
"lgr": "lengoyî",
"nmy": "namuyî",
"ncn": "naunayî",
"pez": "penaniya rojhilatî",
"mfg": "mixiforeyî",
"bei": "bekatî'",
"ydg": "yidghayî",
"cwd": "kriya woods",
"mmn": "mamanwayî",
"nai-kat": "kathlametî",
"cje": "çruyî",
"ark": "arîkapûyî",
"fut": "futuna-anîwayî",
"wad": "wandamenî",
"tew": "tewayî",
"nyo": "nyoroyî",
"bky": "bokyî",
"tjm": "timucuayî",
"ysg": "sonagayî",
"ogu": "ogbronuagumî",
"mnj": "munjî",
"gwd": "gawwadayî",
"fbl": "west albay bikolî",
"uiv": "îyiveyî",
"bkq": "bakairí",
"ibe": "akpesî",
"maa": "mazateciya san jerónimo tecóatl",
"jig": "jinguluyî",
"tvd": "tsuvadî",
"xod": "kokodayî",
"adw": "amondawayî",
"guu": "yanomamöî",
"srh": "sarikolî",
"nam": "ngan'gityemerrî",
"hto": "minica huitotoyî",
"yby": "yaweyuhayî",
"urz": "uru-eu-wau-wau",
"ewo": "ewondoyî",
"cal": "karolînî",
"arr": "arara-karo",
"yup": "yukpayî",
"iai": "îaaiyî",
"wbh": "wandayî",
"dmw": "mudburrayî",
"xel": "keloyî",
"rma": "ramayî",
"zwa": "zayî",
"emi": "mûssaû-emîrakî",
"lai": "lambyayî",
"dim": "dimeyî",
"agu": "agwakatekî",
"zin": "zinzayî",
"kiv": "kimbuyî",
"dbg": "dogul domî",
"bzg": "babuzayî",
"roa-ole": "leoniya kevn",
"bun": "şerbroyî",
"gnn": "gumatjî",
"mhs": "bûrî",
"pln": "palenqueroyî",
"aus-guw": "guwarî",
"tgt": "tagbanwayiya navendî",
"mwv": "mentawaî",
"kke": "kakabeyî",
"pla": "mianî",
"bpr": "koronadal blaanî",
"oty": "tamîliya kevn",
"ywr": "yawuruyî",
"nai-tap": "tapaçultecî",
"sai-sec": "seçurayî",
"pid": "piaroayî",
"mih": "mixteciya çayuco",
"icr": "kreyoliya îngilîzî ya îslanderê",
"hoh": "hobyótî",
"fab": "annoboneseyî",
"ssm": "semnamî",
"ncg": "nisga'ayî",
"mkg": "makî (Çîn)",
"max": "malayiya moluccana bakur",
"zak": "zanakî",
"kxv": "kuviyî",
"xkn": "kayan river kayanî",
"fuy": "fuyugî",
"zpl": "zapotekiya laçixio",
"kwf": "kwara'aeyî",
"aig": "îngilîziya kreyolî ya antîgua û barbud",
"rai": "ramoaainayî",
"otd": "dohoyî",
"bey": "akuwagelî",
"nwr": "nawaruyî",
"tmq": "tumleoyî",
"tgx": "tagişî",
"pbr": "pangwayî",
"loj": "louyî",
"emp": "emberayiya bakurî",
"nho": "takuuyî",
"bco": "kalulî",
"ifb": "batad ifugaoyî",
"jao": "yanyuwayî",
"inc-ohi": "hindiya kevn",
"cup": "cupeñoyî",
"bci": "bawuleyî",
"ngo": "ngonî",
"nun": "anongî",
"yee": "yimasî",
"kxb": "krobuyî",
"hub": "huambisayî",
"nee": "kumakî",
"bec": "iceve-macî",
"smb": "simbarî",
"kuj": "kuriayî",
"ian": "îatmulî",
"cjv": "çuaveyî",
"zne": "zandeyî",
"ssd": "siroî",
"cbn": "nyah kurî",
"lwg": "wangayî",
"bmr": "muinanî",
"kya": "kwayayî",
"xem": "kembayanî",
"acz": "açeronî",
"pav": "wari'",
"egx-dem": "demotîkî",
"bph": "botlixî",
"lev": "lammayî",
"btp": "budibudî",
"bzq": "bulî",
"tdv": "toroyî",
"klq": "rumuyî",
"ojv": "javayiya ontongê",
"bdi": "burunî",
"wya": "wyandotî",
"pbb": "paezî",
"lfn": "lingua franca nova",
"jit": "jitayî",
"amq": "amahaî",
"tcf": "mepaya malînaltepekî",
"ebu": "embuyî",
"rwm": "ambayî",
"pnh": "penrhynî",
"and": "ansus",
"pej": "pomoyiya bakurî",
"kue": "kumanî",
"fip": "fipayî",
"kcn": "nubiyî",
"lol": "mongoyî",
"bwd": "biwaydokayî",
"aia": "arosî",
"zau": "zangskarî",
"pud": "punan aputî",
"ikz": "îkizuyî",
"bgb": "bobongko",
"thk": "tharakayî",
"ymc": "mujiya başûrî",
"mny": "manyawayî",
"xwa": "kwazayî",
"wrs": "warisî",
"nai-tip": "tipaî",
"teu": "sooyî",
"ked": "kereweyî",
"szp": "suaboyî",
"rgr": "resigaroyî",
"bdm": "budumayî",
"nib": "nakameyî",
"lek": "leyponî",
"guh": "guahiboyî",
"bqr": "burusuyî",
"apl": "lipan",
"bnd": "banda",
"tsj": "tşanglayî",
"xme-old": "mediya kevn",
"ppn": "papapanayî",
"mwp": "kala lagaw yayî",
"bfa": "bariyî",
"plh": "paulohî",
"btd": "batakiya dairî",
"ure": "uruyî",
"sty": "tatariya sîberî",
"buy": "mmaniyî",
"xyy": "yorta yortayî",
"xdy": "malayîk dayakî",
"kmj": "kumarbhag pahariayî",
"vun": "vunjoyî",
"cuh": "çukayî",
"lgl": "walayî",
"pna": "punan bah-biauyî",
"now": "nyamboyî",
"gqa": "ga'andayî",
"thm": "thavungî",
"acr": "açiyî",
"cir": "tiriyî",
"mxr": "murîkî (Malezya)",
"swp": "suauyî",
"zor": "zoqueyiya rayón",
"wic": "wiçitayî",
"suw": "sumbwayî",
"bum": "buluyî (Kamerûn)",
"rnw": "rungwayî",
"nak": "nakanayî",
"trs": "trîkuyiya çicahuaxtla",
"lpo": "lipoyî",
"gwe": "gwenoyî",
"cbd": "carijonayî",
"mot": "barî",
"tlv": "taliabuyî",
"xsu": "sanumayî",
"sjl": "mijî",
"pip": "peroyî",
"acx": "erebiya ummanî",
"ybb": "yembayî",
"uln": "unserdeutsch",
"yil": "yindjilandjî",
"ykn": "kua-nsî",
"apu": "apurinã",
"sso": "sissanoyî",
"puc": "punan merapî",
"mwn": "nyamwangayî",
"bpa": "dakakayî",
"mmd": "maonanî",
"bof": "bolonî",
"ati": "attié",
"wji": "warjî",
"lra": "rara bakati'",
"mlu": "toabaîtayî",
"bdd": "bunamayî",
"bfd": "bafutî",
"keu": "akebuyî",
"wod": "wolanî",
"ksb": "şambalayî",
"mff": "nakî",
"azn": "durango nahuatliya rojavayî ya jêrîn",
"asi": "buruwayî",
"ala": "alago",
"tpy": "trumaî",
"zms": "mbesayî",
"kib": "koalibî",
"jun": "juangî",
"guf": "gupapuynguyî",
"liy": "banda-bambariyî",
"sai-pam": "pamiguayî",
"loa": "lolodayî",
"elm": "elemeyî",
"aad": "amalî",
"lcm": "tungagî",
"twh": "tai dónî",
"ppm": "papumayî",
"xmr": "meroytîkî",
"kno": "konoyî (sierra leone)",
"kzz": "kalabrî",
"emw": "emplawasî",
"pnk": "pawnakayî",
"gym": "ngäbereyî",
"mxj": "mijuyî",
"lun": "lundayî",
"kos": "kosraeyî",
"kzx": "kamarianî",
"aan": "anambeyî",
"ukq": "ukwayî",
"tau": "tananaya jorîn",
"anc": "ngas",
"rcf": "kreyola fransî ya réunion",
"knm": "kanamariyî",
"bvy": "baybayanonî",
"jnj": "yemsayî",
"onu": "unuayî",
"bdh": "tara bakayî",
"pij": "pijaoyî",
"mks": "mixteciya silacayoapan",
"juh": "honeyî",
"etx": "etenî",
"sai-chr": "çarruayî",
"aui": "anukî",
"cuc": "usila çinantecî",
"xme-ott": "tatiya kevn",
"njj": "njenî",
"mng": "mnongiya rojhilatî",
"cih": "çinaliyî",
"ard": "arabana",
"val": "vehesî",
"aos": "taykatî",
"peb": "pomoyiya rojhilatî",
"xcb": "kumbrîkî",
"clk": "iduyî",
"aem": "aremî",
"vut": "vuteyî",
"teq": "temeinî",
"siu": "sinagenî",
"xmm": "manado malayî",
"gdj": "kurtjarî",
"xau": "kauwerayî",
"atz": "arta",
"zsr": "zapoteciya rincona başûrî",
"wmt": "walmajarrî",
"ter": "terenoyî",
"sit-sit": "situyî",
"zns": "mangasî",
"kwz": "kwadiyî",
"iki": "îkoyî",
"prq": "aşeninka pereneyî",
"ztq": "zapotekiya quioquitani-quieri",
"wan": "wanî",
"txh": "thrasî",
"ahg": "qimant",
"boe": "mundablî",
"gww": "kwiniyî",
"ior": "înorî",
"ply": "bolyuyî",
"avt": "au",
"zpu": "zapotekiya yalalagî",
"bgv": "warkay-bîpîmî",
"srb": "sorayî",
"huu": "murui huitotoyî",
"zmm": "marimanindjî",
"xme-ker": "kermanî",
"gbv": "gbanuyî",
"awe": "awetí",
"xzh": "zhang-zhungî",
"wow": "wawonî",
"tqo": "toaripî",
"syb": "subaneniya navendî",
"ril": "riangî",
"nqo": "n'koyî",
"laj": "langoyî (Uganda)",
"bbh": "buganî",
"orr": "orumayî",
"gju": "gojriyî",
"ddd": "dongotonoyî",
"yik": "dongşanba laloyî",
"sai-bag": "baguayî",
"tsr": "akeî",
"txe": "totolî",
"nal": "nalikî",
"bez": "kîbenayî",
"jeh": "jehî",
"sem-saf": "safaitîkî",
"mka": "mbreyî",
"nai-jum": "jumaytepequeyî",
"ebg": "ebughuyî",
"cbv": "cacuayî",
"lnl": "bandayiya navendî ya başûr",
"inc-kho": "xolosî",
"tzx": "tabriakî",
"sln": "salînanî",
"ncr": "ncaneyî",
"kqt": "kadazaniya çemê kliasê",
"cbr": "caşibo-cacataiboyî",
"trt": "tunggareyî",
"tyj": "tai doyî",
"kpr": "korafe-yeghayî",
"hmt": "hamtaiyî",
"csi": "coast miwokî",
"bbi": "barombî",
"tvk": "ambrîmiya başûr-rojhilat",
"ynd": "yandruwandhayî",
"sai-sap": "saparayî",
"mik": "mikasukî",
"kid": "koşinî",
"bya": "batakiya palawan",
"awd-ana": "anauyayî",
"yrw": "yarawatayî",
"tnx": "tanemayî",
"lme": "peveyî",
"nbm": "ngbaka ma'boyî",
"myy": "macunayî",
"cao": "çacoboyî",
"anp": "angîka",
"sim": "seimî",
"nha": "nhandayî",
"kqn": "kaondeyî",
"ecr": "eteocretanî",
"crv": "çaurayî",
"bef": "benabenayî",
"aud": "anuta",
"yiy": "yir-yorontî",
"usp": "uspantekoyî",
"sry": "serayî",
"njy": "njyemî",
"mod": "mobilianî",
"mmt": "malalamaî",
"leu": "karayî (Gîneya nû)",
"mbe": "molaleyî",
"txi": "ikpengî",
"snc": "sinaugoroyî",
"noc": "nukî",
"mle": "manambuyî",
"kyr": "kuruayayî",
"ivb": "îbatanî",
"sai-chu": "çuruyayî",
"lsr": "aruopî",
"zaq": "zapotekiya aloapam",
"tef": "teressayî",
"siy": "sivandî",
"nyb": "nyangboyî",
"mpi": "mpadeyî",
"tcq": "kayî",
"kbt": "gabadiyî",
"dka": "dakpayî",
"bff": "bofî",
"ahs": "aşeyî",
"zar": "zapotekiya rincón",
"xct": "tibetiya klasîk",
"afz": "obokuîtayî",
"mnf": "mundanî",
"mpw": "mapidianî",
"gza": "ganzayî",
"acv": "açumawî",
"zmu": "muruwarî",
"zmt": "maringarrî",
"lbc": "lakkiayî",
"gbg": "gbanziriyî",
"des": "desanoyî",
"kud": "awhelawayî",
"agc": "agatuyî",
"tob": "tobayî",
"suc": "subanoniya rojavayî",
"oni": "onînî",
"mne": "nabayî",
"itm": "îtu mbon uzoyî",
"ges": "geser-goromî",
"aza": "azhayî",
"nfl": "aiwooyî",
"tqp": "tomoipî",
"rof": "romboyî",
"ury": "oryayî",
"axg": "mato grosso arára",
"jax": "jambî malayî",
"gkn": "gokanayî",
"tbq-gkh": "gokhyî",
"bkz": "bungku",
"aik": "akeyî",
"tku": "totonaciya nekaksaya jorîn",
"sho": "şangayî",
"crp-rsn": "russenorskî",
"nss": "nalî",
"guo": "guayaberoyî",
"dun": "dusun deyahî",
"bsm": "busamiyî",
"ble": "balanta-kentohe",
"axx": "xaragure",
"sxg": "şixingî",
"apz": "safeyoka",
"awd-pai": "paikonekayî",
"muc": "mbu'",
"kgu": "kobolî",
"dbf": "edopiyî",
"bbp": "bandayiya rojavayî ya navendî",
"loe": "saluanî",
"nux": "mehekî",
"elk": "elkeiyî",
"yaf": "yakayî",
"tiz": "tai hongjinî",
"stu": "samtaoyî",
"nkg": "nekginî",
"koz": "korakî",
"aus-bra": "barranbinyayî",
"tlr": "talîsî",
"spc": "sapeyî",
"sai-pyg": "payaguayî",
"tbq-mil": "milangî",
"klp": "kamasayî",
"crz": "cruzeñoyî",
"boj": "ancamî",
"tan": "tangaleyî",
"svs": "savosavoyî",
"ppq": "peyî",
"ngg": "ngbaka manzayî",
"mpx": "misima-paneatî",
"kcp": "kangayî",
"cbi": "çaçiyî",
"bfc": "bayiya bakurî",
"yec": "yeniçeyî",
"sai-trr": "tarairiú",
"plg": "pilagayî",
"hoe": "horomî",
"yun": "benayî",
"any": "anyî",
"yis": "yisî",
"ush": "uşojoyî",
"tav": "tatuyoyî",
"sve": "serilî",
"gob": "playeroyî",
"nkz": "nkarî",
"moc": "mocovî",
"jko": "kuboyî",
"reg": "karayî (Tanzanya)",
"hul": "hulayî",
"chh": "çinookî",
"yub": "yugambalî",
"tbg": "tayrorayiya bakur",
"mdc": "maleyî",
"kuo": "kumukioyî",
"mvy": "indus kohistanî",
"foi": "foiyî",
"coz": "çoçotecî",
"bim": "bimobayî",
"agf": "argunî",
"kmx": "wabodayî",
"mtl": "montolî",
"kjr": "kuruduyî",
"kcb": "kawaçayî",
"ibn": "îbinoyî",
"scb": "çutî",
"atx": "arutanî",
"mvt": "mpotovoroyî",
"kxa": "kayrîrûyî",
"boq": "bogayayî",
"zpx": "zapotekiya san baltazar loxiça",
"wnc": "wantoatî",
"itd": "tidongiya başûrî",
"xog": "sogayî",
"ann": "obolo",
"unr": "mundarî",
"bzh": "mapos buangî",
"tye": "kyengayî",
"kyq": "kengayî",
"kwa": "dâwî",
"bou": "bondeyî",
"aul": "aulua",
"zty": "zapotekiya yatee",
"wrp": "waropenî",
"mmi": "musarî",
"buq": "bremî",
"auj": "awjîla",
"gtu": "aghu tharrnggalayî",
"zil": "zialoyî",
"tlb": "tobeloyî",
"mty": "nabî",
"lbz": "lardilî",
"gdd": "gêdagêdî",
"mdx": "dizinî",
"bgt": "bugotûyî",
"tpc": "azoyú me'phaayî",
"jmb": "zumbunî",
"tdn": "tondanoyî",
"rng": "rongayî",
"mtm": "matorî",
"djd": "jaminjungî",
"gol": "golayî",
"doz": "dorzeyî",
"nmk": "namakurayî",
"mnt": "maykulanî",
"lbl": "libon bikolî",
"gde": "gudeyî",
"bxs": "busamî",
"xmd": "mbudumî",
"kdq": "koçî",
"cah": "cahuaranoyî",
"bza": "bandiyî",
"aes": "alseayî",
"dsq": "tadaksahakî",
"tmj": "samarokenayî",
"att": "pamplona atta",
"xhd": "hadramî",
"brp": "barapasiyî",
"omw": "tairorayiya başûr",
"nif": "nekî",
"iyo": "mesakayî",
"luq": "lucumiyî",
"kqz": "koranayî",
"coc": "cocopayî",
"amj": "amdang",
"tlk": "talokî",
"spi": "saponî",
"nnf": "ngaingî",
"mlj": "miltuyî",
"luz": "luriya başûrî",
"xas": "kamassî",
"cua": "cuayî",
"afd": "andaî",
"yxl": "yarlî",
"sit-tgm": "tangamî",
"nga": "ngbakayî",
"mrg": "misingî",
"igm": "kanggapeyî",
"gop": "yeretuarî",
"myh": "makahî",
"kpg": "kapîngamarancîyî",
"ero": "horpayî",
"eto": "etonî (Kamerûn)",
"bcq": "bençî",
"srr": "sererî",
"mqz": "malasangayî",
"hui": "huliyî",
"cap": "çipayayî",
"uar": "tayrumayî",
"mox": "molimayî",
"nai-ipa": "ipaî",
"arh": "arhuako",
"wdj": "wadjiginyî",
"tem": "temneyî",
"snn": "sionayî",
"mxk": "monumboyî",
"kru": "kurukhî",
"inc-mas": "asamiya navîn",
"zoo": "zapotekiya asunción mixtepec",
"wme": "wambuleyî",
"pru": "puragî",
"qwt": "kwalhioqua-tlatskanaî",
"gbb": "kaytetyeyî",
"bux": "boghomî",
"atm": "ata",
"abn": "abuayî",
"zab": "zapotekiya san juan guelavia",
"wny": "wanyî",
"sai-qtm": "quitemoyî",
"mmf": "mundatî",
"gdm": "laalî",
"kxi": "keningau murutî",
"dwr": "dawroyî",
"bfw": "bondoyî",
"aux": "aurá",
"ttv": "titanî",
"nrz": "lalayî (Gîneya nû)",
"gbp": "gbaya-bossangoayî",
"awn": "awngî",
"zia": "ziayî",
"way": "wayanayî",
"sut": "subtiabayî",
"rog": "roglayiya bakurî",
"ono": "onondagayî",
"mbj": "nadëbî",
"mls": "masalitî",
"bbj": "ghomalayî'",
"dyi": "djiminiyî",
"bgg": "bugunî",
"ail": "aîmeleyî",
"wir": "wirafedî",
"cla": "ronî",
"ost": "osatuyî",
"kkr": "kir-balarî",
"dgb": "bunogeyî",
"yha": "bahayî",
"mcd": "şaranahuayî",
"rwk": "rwayî",
"ifu": "mayoyao ifugaoyî",
"lig": "ligbiyî",
"jek": "jeri kuoyî",
"ghs": "guhu-samaneyî",
"bdn": "baldemuyî",
"xkf": "xengxayî",
"tsb": "tsamaî",
"sle": "şolagayî",
"nai-spt": "sahaptinî",
"nas": "nasioyî",
"mhd": "mbuguyî",
"kfa": "kodavayî",
"jur": "jurúnayî",
"goa": "guroyî",
"efa": "efaiyî",
"aqn": "altaya bakurî",
"tjs": "tujyayiya başûrî",
"tdy": "tadyawanî",
"los": "loniuyî",
"jka": "kaerayî",
"afo": "eloyî",
"cba-cat": "catio çibçayî",
"bsn": "barasanayî",
"ama": "amanayé",
"sai-san": "sanavironî",
"prc": "paraçî",
"nej": "nekoyî",
"cms": "messapicî",
"kyl": "kalapuyayî",
"coj": "coçimiyî",
"byr": "baruyayî",
"aey": "ameleyî",
"aus-ynk": "yangkaalî",
"yta": "taluyî",
"pay": "peç",
"lse": "lusengoyî",
"cub": "cubeoyî",
"gac": "andamaniya mezin a têkel",
"yry": "yarluyandî",
"tgp": "tangoayî",
"mjt": "sawriya pahariayî",
"phl": "phalurayî",
"ngt": "ngeqî",
"xnr": "kangrî",
"ciy": "çaimayî",
"bca": "bayiya navendî",
"aog": "angoram",
"yrn": "yerongî",
"skz": "sekarî",
"pia": "pima bajoyî",
"mjb": "makaleroyî",
"hot": "hoteyî",
"cbt": "çayahuitayî",
"aor": "aore",
"uve": "uveyiya rojava",
"pbv": "pnarî",
"mkr": "malasî",
"qvy": "kueyuyî",
"nai-chi": "çiquimulillayî",
"bhi": "bhilalî",
"erg": "sieyî",
"plo": "popolucayiya oluta",
"aun": "molmo one",
"kun": "kunamayî",
"laz": "aribwatsayî",
"waj": "waffayî",
"xsi": "sioyî",
"ibr": "îbuoroyî",
"zaf": "zapotekiya ayoquesco",
"lbq": "wamparî",
"keg": "teseyî",
"nyt": "nyawaygî",
"akc": "mpur",
"mnx": "manikionî",
"kmq": "kwamayî",
"khz": "keaparayî",
"ilk": "îlongotî",
"gaj": "gadsupî",
"aqp": "atakapa",
"kgr": "abunî",
"zpf": "zapotekiya san pedro quiatoni",
"sok": "sokoroyî",
"odu": "odualî",
"muh": "mündü",
"lbb": "labelî",
"knl": "keninjalî",
"bna": "boneratî",
"auy": "auyana",
"ade": "adeleyî",
"zpw": "zapotekiya zaniza",
"opm": "oksapminî",
"mug": "musguyî",
"hob": "mariyiya awistronezî",
"ywl": "laluyiya rojavayî",
"ksx": "kêdangî",
"gbq": "gbaya-bozoumî",
"xbm": "bretoniya navîn",
"kmn": "awtuwî",
"ziw": "zigulayî",
"wbm": "wayî",
"tgo": "sudestî",
"opo": "opaoyî",
"lan": "laruyî",
"xuu": "khweyî",
"gid": "gidarî",
"bvj": "baanî",
"zzj": "zuojiang zhuangî",
"wpc": "wirö",
"rnn": "roonî",
"don": "dourayî",
"bhj": "bahingî",
"lib": "likumî",
"akj": "jeru",
"awd-gnu": "guinauyî",
"dux": "duunî",
"bqp": "busayî",
"nsk": "naskapî",
"mqb": "mbukoyî",
"kwl": "kofyarî",
"nai-jtp": "jutiapayî",
"grd": "guruntumî",
"llp": "efateyiya bakur",
"agy": "altaya başûrî",
"yar": "yabaranayî",
"khc": "tukang besiya bakur",
"sbl": "sambaliya botolan",
"crc": "lonwolwolî",
"xkg": "kagoroyî",
"hdn": "haidayiya bakurî",
"sai-ctq": "catuquinaruyî",
"bbb": "barayî",
"amr": "amarakaerî",
"yky": "yakomayî",
"bsx": "yangkamî",
"tmz": "tamanakuyî",
"zsa": "sarasirayî",
"nig": "ngalakanî",
"khl": "lusiyî",
"xow": "kowakî",
"hem": "hembayî",
"pga": "erebiya jubayê",
"bhm": "batharî",
"bzb": "andioyî",
"yko": "yasayî",
"sax": "sayî",
"ypg": "pholayî",
"nnh": "ngiemboonî",
"mpd": "maçinereyî",
"vic": "kreyoliya giravên virginê",
"kdp": "kaningdon-nindemî",
"sit-cha": "çairelî",
"yes": "yeskwayî",
"uur": "urayî (Vanuatu)",
"mhu": "taraonî",
"slu": "selaruyî",
"xtd": "mixtekiya diuxi-tilantongo",
"utr": "etuloyî",
"bnv": "benerafî",
"ydk": "yoidikî",
"trr": "tauşîroyî",
"srk": "serudung murutî",
"xpo": "poçutekî",
"nkx": "nkorooyî",
"kes": "kugboyî",
"kgm": "karipúnayî",
"cax": "çiquitanoyî",
"big": "biangaî",
"vil": "vilelayî",
"dtu": "tebul ure dogonî",
"tty": "sikaritaî",
"rmg": "norweciya traveller",
"mlw": "molokoyî",
"biq": "bipî",
"ylu": "aribwaungî",
"wga": "wagayayî",
"tnz": "ten'ednî",
"qum": "sipakapenseyî",
"pue": "puelçeyî",
"nop": "numanggangî",
"mzq": "mori atasî",
"awd-kus": "kustenauyî",
"idi": "îdî",
"kzf": "da'a kailiyî",
"bib": "bîssayî",
"zad": "zapotekiya cajonos",
"waz": "wampurî",
"tll": "tetelayî",
"pur": "puruborayî",
"nih": "nyihayî",
"tnk": "kwamerayî",
"ilv": "îlueyî",
"dmn-dam": "damayî (Sierra leone)",
"cba-ata": "atanquesî",
"abu": "abureyî",
"zpt": "zapotekiya san vicente coatlan",
"aaw": "solongyî",
"opy": "ofayeyî",
"dtb": "labuk-kinabatangan kadazanî",
"ikt": "înuvialuktunî",
"auu": "auye",
"adi": "adî",
"wsk": "waskiayî",
"tix": "tiwayiya başûrî",
"atu": "reel",
"mui": "musî",
"iso": "îsokoyî",
"gya": "gbayayiya bakur-rojava",
"auw": "awyî",
"zik": "zimakanî",
"sai-way": "wayumarayî",
"sue": "suenayî",
"sai-opo": "opónî",
"srf": "nafî",
"qxs": "kiangiya başûrî",
"ggt": "gituayî",
"bcd": "babariya bakur",
"xam": "ǀxamî",
"wiu": "wiruyî",
"rro": "roroyî",
"osi": "osingî",
"mvb": "mattolî",
"mjk": "matukarî",
"lln": "leleyî (Çad)",
"kiy": "kirikiriyî",
"sit-jap": "japhugî",
"tds": "doutaî",
"blf": "buol",
"akt": "akolet",
"apb": "sa'a",
"awd-mpr": "maypureyî",
"jee": "jerungî",
"saj": "sahuyî",
"pkn": "pakanhayî",
"kkx": "kohinî",
"nai-cal": "calusayî",
"bgf": "banganduyî",
"ybm": "yabenî",
"bhq": "tukang besî başûr",
"mxe": "mele-filayî",
"lom": "loomayî",
"hka": "kaheyî",
"emn": "emanî",
"boi": "barbarenyoyî",
"aie": "amarayî",
"anh": "nendî",
"mej": "meyahî",
"kcl": "kalayî",
"hro": "haroiyî",
"enc": "enî",
"amy": "amî",
"yde": "yangum deyî",
"taj": "tamangiya rojhilatî",
"sar": "saravekayî",
"szb": "ngalumî",
"inm": "minaeanî",
"kwh": "kowyayî",
"hed": "herdeyî",
"cuy": "cuitlatecî",
"yvt": "yaviteroyî",
"sgw": "sebat bet gurageyî",
"nph": "phomî",
"mjs": "mişipî",
"mda": "madayî (Nîjerya)",
"wat": "kaninuwayî",
"blo": "anîî",
"yss": "yessan-mayoyî",
"ona": "selk'namî",
"pny": "pinyinî",
"noe": "nimadî",
"crm": "kriya mûsî",
"hhy": "hoyahoyayî",
"bzz": "evantî",
"inc-apa": "apabhramsayî",
"uuu": "uyî",
"seu": "serui-lautî",
"pbi": "podokoyî",
"mhc": "moçoyî",
"kzw": "kaririyî",
"faa": "fasuyî",
"sai-chi": "çirinoyî",
"yab": "yuhupî",
"vit": "vitî",
"myl": "momayî",
"crb": "island caribî",
"frq": "forakî",
"cog": "çongî",
"brk": "birgidî",
"waq": "wagemanî",
"nuy": "nunggubuyuyî",
"mdb": "morigî",
"idu": "îdomayî",
"fun": "fulniôî",
"dao": "daai çinî",
"wnb": "wanambreyî",
"skv": "skouyî",
"sai-pur": "purukotó",
"nyi": "nyimangî",
"kwn": "kwangaliyî",
"kec": "keigayî",
"gbu": "gagaduyî",
"dnj": "danî",
"bus": "bokobaruyî",
"ado": "abuyî",
"zpn": "zapotekiya santa ines yatzeçi",
"aml": "war-jaîntî",
"mij": "mungbamî",
"lbu": "labuyî",
"ken": "kenyangî",
"ipo": "îpikoyî",
"zam": "zapotekoyiya mahuatlana navendî",
"wac": "wasko-wişramî",
"sww": "sowayî",
"msu": "musomî",
"rkm": "markayî",
"slp": "lamaholotî",
"gso": "gbayayiya başûr-rojava",
"ahb": "axamb",
"kit": "agobî",
"bfi": "zimanê destan ê brîtanî",
"taz": "toçoyî",
"swi": "suî",
"prm": "kibirî",
"gis": "gizigayiya bakur",
"doe": "doeyî",
"abj": "aka-beayî",
"hnh": "ǁaniyî",
"roo": "rotokasî",
"nlv": "nahwatliya orizaba",
"npl": "nahwatliya pueblaya başûr-rojhilatî",
"llc": "leleyî (Gîne)",
"fkk": "kirya-konzelî",
"mde": "bura mabangî",
"bdl": "bajwayiya endonezyayî",
"bss": "akooseyî",
"scw": "şayî",
"lsm": "saamiayî",
"pqa": "pa'ayî",
"nmq": "nambyayî",
"mbq": "maysînî",
"jib": "jibuyî",
"bhs": "buwalî",
"sjo": "xibeyî",
"abw": "pal",
"nxa": "naueteyî",
"mck": "mbundayî",
"jmx": "juxtlahuaca mixteciya rojavayî",
"clu": "caluyanunî",
"ybo": "yabongî",
"tuo": "tukanoyî",
"nml": "ndemlî",
"mfx": "meloyî",
"lop": "lopayî",
"kzv": "komyandaretî",
"kct": "kaianî",
"has": "haislayî",
"mmw": "emayî",
"cyb": "cayubabayî",
"bcj": "bardî",
"awd-ama": "amarizanayî",
"ylr": "yalarnngayî",
"ago": "taînaeyî",
"prw": "parawenî",
"enw": "enwangî",
"kog": "coguiyî",
"bst": "basketoyî",
"ymo": "yangum monî",
"uda": "udayî",
"tma": "tamayî (Çad)",
"sre": "sarayî",
"nbv": "ngamamboyî",
"mcv": "minanibaî",
"koh": "koyoyî",
"zbt": "batuî",
"anb": "andoa",
"yaw": "yawalapitî",
"tnc": "tanimuca-retuarã",
"sbx": "seberuangî",
"yhl": "hlepho phowayî",
"bzv": "bebeyî",
"anf": "anîmere",
"yet": "yetfayî",
"pir": "piratapuyoyî",
"nmw": "nimoayî",
"nhb": "bengî",
"tbo": "tawalayî",
"szd": "seruyî",
"poy": "pogoloyî",
"bve": "malayiya berau",
"ctm": "çitimaçayî",
"aaf": "aranadanî",
"mkh-mvi": "viyetnamiya navîn",
"mnd": "mondeyî",
"mup": "malvî",
"kse": "kuniyî",
"btf": "birgîtî",
"aac": "ariyî",
"tex": "tennetî",
"kbb": "kaxuyanayî",
"ula": "fungwayî",
"dsh": "daasanaçî",
"zps": "zapotekiya coatlan",
"wnp": "wanapî",
"scs": "slaveyiya bakur",
"lid": "nyindrouyî",
"kvi": "kwangî",
"idb": "îndo-portegîzî",
"bdt": "bokotoyî",
"ate": "atemble",
"zpi": "zapotekiya santa maria quiegolani",
"tvy": "timor pidginî",
"sol": "solosî",
"rjs": "rajbanşî",
"ogc": "ogbahî",
"mth": "mungguî",
"dmc": "gavakî",
"zaj": "zaramoyî",
"sss": "soyî",
"nai-okw": "okwanuçuyî",
"hia": "lamangî",
"gyg": "gbayiyî",
"duy": "dicamay agtayî",
"sgt": "brokpakeyî",
"yix": "axi yî",
"tcx": "todayî",
"sui": "sukî",
"phi-nag": "nagtipunan agtayî",
"ixc": "îxcatecî",
"giz": "gizigayiya başûr",
"baa": "babatanayî",
"akm": "aka-bo",
"wiy": "wiyotî",
"tnt": "tontemboanî",
"uta": "otankî",
"nhc": "nahwatliya tabasco",
"mph": "maungî",
"led": "lenduyî",
"kiz": "kisiyî",
"dva": "duauyî",
"bvr": "burarrayî",
"yig": "wusa nasuyî",
"neb": "tourayî",
"shv": "şehrî",
"nnm": "namiayî",
"nai-wji": "jicaqueyiya rojavayî",
"gve": "duwetî",
"yln": "buyangiya langnî",
"blq": "baluan-pam",
"jiv": "şuarî",
"quv": "sakapultekoyî",
"ynk": "naukanskî",
"mgy": "mbungayî",
"kpx": "koiariyiya çiyayî",
"ktz": "juǀ'hoanî",
"ofu": "efutopî",
"wes": "cameroon pidginî",
"dba": "bangi meyî",
"aab": "alumu-tesuyî",
"bkj": "pande",
"ndg": "ndengerekoyî",
"bbo": "konabereyî",
"zka": "kaimbulawayî",
"hgm": "haiǁomî",
"alv-ama": "amasî",
"ttm": "tutçoneyiya bakurî",
"nen": "nengoneyî",
"zmf": "mfinuyî",
"ruf": "luguruyî",
"kfq": "korkuyî",
"tmd": "haruaî",
"eot": "eotileyî",
"sem-amm": "ammoniteyî",
"tdk": "tambasî",
"sba": "ngambayî",
"lue": "luvaleyî",
"khq": "koyra çiiniyî",
"doc": "kamiya bakurî",
"sai-cul": "cullî",
"ano": "andoque",
"yww": "yawarawargayî",
"uji": "tanjijilî",
"ypz": "phuzayî",
"nsq": "miwokiya sierraya bakurî",
"sgb": "mag-ançî aytayî",
"ish": "esanî",
"aoi": "anîndîlyakwa",
"uri": "urimî",
"yer": "tarokî",
"shb": "ninamî",
"kpj": "karajayî",
"hoz": "hozoyî",
"twm": "tawang monpayî",
"mqo": "modoleyî",
"meo": "malayiya kedah",
"bbv": "karnaî",
"agl": "fembeyî",
"ctg": "çittagonianî",
"tkq": "teeyî",
"wmd": "mamaindeyî",
"kjn": "kunjenî",
"kmg": "kâteyî",
"bqq": "biritaiyî",
"aai": "arîfama-mînîafîayî",
"swj": "sirayî",
"puw": "puluwatî",
"rin": "nunguyî",
"bdo": "moromî",
"khj": "kuturmiyî",
"fvr": "furî",
"dbq": "dabayî",
"blr": "blang",
"mfi": "wandalayî",
"xsl": "slaveyiya başûr",
"pww": "pwoyiya bakurî",
"knp": "kwanjayî",
"gbi": "galelayî",
"bqc": "bokoyî",
"zpr": "zapotekiya santiago xanica",
"sai-tim": "timoteyî",
"rki": "rakhineyî",
"ogb": "ogbiayî",
"mpv": "mungkipî",
"ker": "kerayî",
"iqu": "îquitoyî",
"gou": "gavarî",
"bvf": "boorî",
"tmb": "avavayî",
"was": "waşoyî",
"tkp": "tîkopyayî",
"reb": "rembongî",
"ogo": "khanayî",
"diu": "gcirikuyî",
"orw": "oro wînî",
"nlx": "nahalî",
"vme": "maselayiya rojhilat",
"ltn": "latundê",
"bvx": "baboleyî",
"aci": "aka-karî",
"rug": "rovyanayî",
"kqs": "kissiya bakurî",
"dmu": "dubuyî",
"bwr": "burayî",
"bkc": "baka",
"akf": "akpayî",
"wut": "wutungî",
"she": "şekoyî",
"pma": "paamayî",
"mgi": "jilî",
"gvl": "gulayî",
"dyy": "dyaabugayî",
"aiy": "aliyî",
"xin": "xincayî",
"skf": "sakirabiayî",
"zmp": "mbuunî",
"bex": "jûr modoyî",
"mea": "menkayî",
"kfc": "konda-dorayî",
"kbw": "kaîepî",
"cto": "embera-catioyî",
"cam": "cemuhî",
"bae": "baré",
"tce": "tutçoneyiya başûrî",
"nfu": "mfumteyî",
"lcq": "luhuyî",
"oco": "korniya kevn",
"ktg": "kalkatunguyî",
"sja": "epenayî",
"col": "columbia-wenatçiyî",
"alx": "amol",
"wca": "yanomamî",
"xxt": "tamborayî",
"auk": "heyo",
"cuo": "cumanagotoyî",
"sai-and": "andoqueroyî",
"yva": "yawayî",
"due": "umiray dumaget agtayî",
"kml": "tanudan kalingayî",
"ptr": "piamatsinayî",
"blx": "mag-îndî ayta",
"gcl": "kreyoliya îngilîzî ya grenadî",
"ese": "ese ejjayî",
"uvh": "urî",
"nsm": "semayî",
"nin": "ninzoyî",
"hra": "hrangkholî",
"nai-taw": "tawasayî",
"sbi": "setî",
"mvq": "moereyî",
"mqg": "malayiya kota bangun kutai",
"uwa": "kuku-uwanhî",
"iow": "çiwereyî",
"bid": "bidiyoyî",
"yuk": "yukî",
"teh": "tehuelçeyî",
"mkc": "siliputî",
"zle-ono": "novgorodiya kevn",
"wmh": "waima'ayî",
"tio": "teopî",
"yae": "pumeyî",
"mqn": "moroneneyî",
"zma": "mandayî (Awistralya)",
"kdc": "kutuyî",
"kyt": "kayagarî",
"mps": "dadibî",
"byn": "blinî",
"cns": "asmatiya navendî",
"utp": "abayî",
"zpc": "zapotekiya çoapan",
"den": "slaveyî",
"pwo": "pwoyiya rojavayî",
"mub": "mubî",
"mev": "manoyî",
"bij": "kwankayî",
"kyo": "kelonî",
"inb": "îngayî",
"aph": "athpare",
"zas": "zapotekiya santo domingo albarradas",
"ral": "ralteyî",
"irn": "îrantxeyî",
"dge": "degenanî",
"boa": "borayî",
"adj": "adyukruyî",
"zal": "zauzouyî",
"wax": "watamî",
"tca": "tîkunayî",
"agv": "remontado agta",
"omg": "omaguayî",
"xru": "marriammuyî",
"khn": "khandeşiyî",
"gji": "gejiyî",
"ayd": "ayabadhu",
"zmb": "zimbayî",
"toj": "tojolabalî",
"syk": "sukurî",
"bnf": "masiwang",
"glw": "glavdayî",
"bav": "babungoyî",
"akx": "aka-kede",
"ots": "otomiya estado de meksîka",
"mjj": "mawakî",
"leh": "lenjeyî",
"kss": "kissiyiya başûrî",
"kbz": "duhwayî",
"sth": "şeltayî",
"myf": "bambassî",
"ald": "alladînî",
"ego": "eggonî",
"cbu": "candoşi-şaprayî",
"tmc": "tumakî",
"pgs": "pangsengî",
"ndz": "ndogoyî",
"knd": "kondayî",
"haj": "hajongî",
"cmi": "embera-çamiyî",
"mlp": "bargamî",
"yat": "yambetayî",
"bsg": "başkardiyî",
"pek": "pençalî",
"lwo": "luwoyî",
"kpk": "kpanî",
"kdx": "kamî",
"cug": "cungî",
"anx": "andra-hus",
"sey": "secoyayî",
"pio": "piapocoyî",
"jgo": "ngombayî",
"miy": "mixteciya ayutla",
"gyn": "kreyoliya îngilîzî ya guyanî",
"hoa": "hoavayî",
"amx": "anmatyerre",
"tdm": "tarumayî",
"sza": "semelayî",
"pit": "pitta-pittayî",
"nsz": "nisenanî",
"mis-mmd": "mîmiya decorseyî",
"mky": "makianiya rojhilat",
"kdj": "karamojongî",
"hre": "hreyî",
"bie": "bepourî",
"ena": "apaliyî",
"vgr": "vaghrî",
"sze": "sezeyî",
"not": "nomatsiguengayî",
"mif": "mofu-gudurî",
"mfp": "malayiya makassar",
"kul": "kulereyî",
"huo": "huyî",
"biu": "biete",
"twe": "teiwayî",
"stv": "silt'eyî",
"mqj": "mamasayî",
"kug": "kupayî",
"chw": "çuaboyî",
"zkr": "zakhringî",
"waw": "waiwaî",
"ntp": "tepehuaniya bakurî",
"pmi": "pumiya bakurî",
"hux": "nüpode huitotoyî",
"mor": "moroyî",
"mha": "mandayî (Hindistan)",
"thd": "kuuk thaayorreyî",
"dgz": "dagayî",
"mae": "bo-rukulî",
"sjg": "assangorî",
"zpg": "zapotekiya guevea de humboldt",
"tcz": "thado çinî",
"nyv": "nyulnyulî",
"aso": "dano",
"bld": "bolango",
"snk": "soninkeyî",
"sai-mar": "maratinoyî",
"laf": "lafofayî",
"krk": "kerekî",
"gyl": "gayilî",
"ksr": "borongî",
"avi": "avîkam",
"wah": "watubelayî",
"tpl": "tlacoapa me'phaayî",
"mfr": "marrithiyelî",
"khr": "khariayî",
"giq": "gelaoyiya hêşîn",
"aye": "ayere",
"owl": "weylsiya kevn",
"zsu": "sukurumî",
"nhk": "nahwatliya cosoleacaque",
"law": "laujeyî",
"izr": "îzereyî",
"goi": "gobasiyî",
"wog": "wogamusinî",
"ttu": "torauyî",
"dts": "toro so dogonî",
"mcw": "mawayî",
"azc-ktn": "kitanemukî",
"ddj": "jaruyî",
"dma": "dumayî",
"bkh": "bakoko",
"aqz": "akuntsu",
"pdo": "padoeyî",
"nnb": "nandeyî",
"dzl": "dzalayî",
"bax": "bamumî",
"xiy": "xipayayî",
"tct": "t'enî",
"lht": "lo-togayî",
"kkk": "kokotayî",
"lot": "lotukoyî",
"tdc": "embera-tadó",
"bjc": "barijî",
"aew": "ambakîç",
"tkm": "takelmayî",
"sbp": "sanguyî (Tanzanya)",
"sai-ptg": "patagónî",
"lgk": "neververî",
"ybj": "haşayî",
"amg": "amurdag",
"yns": "yansî",
"udu": "udukî",
"mwm": "sarî",
"ums": "pendauyî",
"wii": "minidienî",
"pbn": "kpasamî",
"cuq": "cunî",
"bbd": "bauyî",
"aty": "aneityum",
"ngc": "ngombeyî (Kongo)",
"kxo": "kanoeyî",
"ags": "esîmbî",
"bxb": "belanda borî",
"urn": "uruangnirinî",
"mis-mmn": "mîmiya naçtigalî",
"hru": "hrusoyî",
"apt": "apatanî",
"yon": "yongkomî",
"vag": "vaglayî",
"vkl": "kulisusuyî",
"kxh": "karoyî",
"jab": "hyamî",
"smg": "simbalî",
"asj": "nsarî",
"knk": "kurankoyî",
"kaf": "katsoyî",
"yml": "îamaleleyî",
"brt": "bitareyî",
"bao": "waîmaha",
"tla": "tepehuaniya başûr-rojavayî",
"pmj": "pumiya başûrî",
"nuf": "nusuyî",
"mrl": "mortlockeseyî",
"man": "mandingoyî",
"ign": "îgnacianoyî",
"bzn": "boanoyî (Maluku)",
"xss": "assanî",
"wne": "wanecî",
"thf": "thangmî",
"kjb": "q'anjob'alî",
"sai-muc": "mucuçî",
"msw": "mansoankayî",
"kem": "kemakî",
"bnp": "bolayî",
"aqm": "atohwaim",
"zat": "zapotekiya tabaa",
"sov": "sonsorolî",
"lhn": "lahananî",
"kjq": "keresiya rojavayî",
"dei": "demisayî",
"kwi": "awa-cuaiquerî",
"aez": "aekayî",
"zay": "zayse-zergullayî",
"una": "watutiya bakur",
"ctl": "tlacoatzintepec çinantecî",
"squ": "squamîşî",
"omb": "ombayî",
"sur": "mwaghavulî",
"umc": "marrusînî",
"lbn": "lametî",
"phi-din": "dinapigue agtayî",
"buw": "bubiyî",
"ahn": "ahàn",
"zrn": "zirenkelî",
"srg": "sulodî",
"ora": "orohayî",
"mtn": "matagalpayî",
"kco": "kinalaknayî",
"hmd": "a-hmaoyî",
"woc": "wogeoyî",
"tqr": "toronayî",
"rbb": "rumaî palaungî",
"ott": "otomiya temoaya",
"nhi": "nahwatliya zacatlan-ahuacatlan-tepetzintla",
"mgk": "mawesî",
"dus": "dumiyî",
"bzu": "burmesoyî",
"bbu": "bakulungî",
"trn": "trinitarioyî",
"sdp": "şerdukpenî",
"sbv": "sabineyî",
"sai-jrj": "jirajirayî",
"gvs": "gumawanayî",
"dnn": "dzuunî",
"tte": "bwanabwanayî",
"bcf": "bamuyî",
"xir": "xiriânayî",
"two": "tswapongî",
"sai-tab": "tabancaleyî",
"lgq": "logbayî",
"klm": "kolomî",
"ngs": "gvokoyî",
"eka": "ekajukî",
"dbw": "bankan tey dogonî",
"aly": "alyawarr",
"yad": "yaguayî",
"ndp": "ndoyî",
"mcr": "menyayî",
"xon": "konkombayî",
"kka": "kakandayî",
"hao": "haköyî",
"ckl": "cibakî",
"jbk": "barikewayî",
"abc": "ambala ayta",
"yaa": "yaminahuayî",
"sny": "saniyo-hiyeweyî",
"mmc": "miçoacan mazahuayî",
"xkq": "koronî",
"had": "hatamî",
"sai-cop": "copallenî",
"swy": "saruayî",
"nid": "ngandî",
"mhi": "ma'dî",
"cpo": "kpeeyî",
"anz": "anem",
"unz": "unde kailî",
"sai-tpr": "taparitayî",
"sed": "sedangî",
"fpe": "piçinglisî",
"mxy": "mixteciya noçixtlana başûr-rojhilatî",
"bfz": "mahasu paharî",
"sai-esm": "esmeraldayî",
"urv": "uruavayî",
"seh": "senayî",
"spx": "pîkeneyiya başûr",
"cag": "nivacleyî",
"zkk": "karankawayî",
"bkl": "berik",
"yut": "yopnoyî",
"vai": "vaî",
"slw": "sialumî",
"noj": "nonuyayî",
"caf": "karrieriya başûrî",
"fag": "finonganî",
"ibh": "bihî",
"awt": "araweté",
"tyx": "teke-tyeeyî",
"mog": "mongondowî",
"knt": "katukinayî",
"bwm": "biwatî",
"apr": "arop-lokep",
"atr": "waîmîrî-atroarî",
"mtt": "motayî",
"pym": "fyamî",
"dlk": "dahalikî",
"bzl": "boanoyî (sulawesî)",
"abp": "abenlen ayta",
"zaw": "zapotekiya mitla",
"svm": "slavomolisanoyî",
"zpz": "zapotekiya texmelucan",
"tit": "tiniguayî",
"rax": "rangî",
"asx": "muratayak",
"mvr": "marauyî",
"lha": "lahayî (Viyetnam)",
"irh": "îrarûtûyî",
"sai-gay": "gayónî",
"awi": "aekyom",
"mcy": "watutiya başûr",
"tpt": "tlaçiçilco tepehuayî",
"sto": "stoneyî",
"rea": "rerauyî",
"aom": "ömie",
"tlx": "khehekî",
"mrx": "dineorî",
"oro": "orokoloyî",
"acl": "akar-baleyî",
"wbw": "woî",
"otn": "otomiya tenango",
"gia": "kitjayî",
"ats": "gros ventre",
"tri": "trió",
"zmz": "mbandjayî",
"yzg": "e'ma buyangî",
"bwa": "bwatûyî",
"bcw": "banayî",
"ywt": "xişanba laloyî",
"tsc": "tswayî",
"gmw-tsx": "saksoniya transylvanyayê",
"fap": "palorî",
"nwa": "nawathinehenayî",
"mue": "media lenguayî",
"kcx": "kaçama-ganjuleyî",
"dah": "gwahatikeyî",
"ekg": "ekariyî",
"cbc": "carapanayî",
"bxw": "bankayî",
"ygw": "yagwoiayî",
"tbr": "tumtumî",
"tgw": "tagwana senoufoyî",
"pnr": "panimî",
"njt": "ndyuka-trio pidginî",
"vnk": "lovonoyî",
"kma": "konniyî",
"hal": "halangî",
"awc": "cicipu",
"bch": "bariyayî",
"gfk": "patpatarî",
"ule": "luleyî",
"vkm": "kamakanî",
"xht": "hattîkî",
"btz": "batakiya alas-kluet",
"dti": "ana tinga dogonî",
"yre": "yaoureyî",
"udj": "ujirî",
"tpu": "tampuanî",
"ung": "ngarinyinî",
"xjb": "minjungbalî",
"slz": "ma'yayî",
"kpe": "kpelleyî",
"xla": "kamulayî",
"kpc": "curripacoyî",
"bvz": "bawziyî",
"ets": "yekheeyî",
"bbn": "uneapayî",
"tvw": "sedoayî",
"crp-spp": "pidginiya samoan plantation",
"ngq": "ngoremeyî",
"mil": "mixteciya peñoles",
"sai-chn": "çanayî",
"kvu": "yinbaw karenî",
"txx": "tatanayî",
"lva": "maku'ayî",
"gel": "fakkanciyî",
"bom": "beromî",
"cvn": "valle nacional çinantecî",
"sya": "siangî",
"bql": "bilakurayî",
"arv": "arbore",
"sai-yup": "yupuayî",
"vnp": "vunapuyî",
"sbb": "simboyî",
"gmw-pro": "proto-cermeniya rojava",
"nbn": "kurî",
"wbv": "wajarrî",
"mwh": "moûk-arîakî",
"bwp": "mandobo bawahî",
"fie": "fyerî",
"dal": "dahaloyî",
"aba": "abeyî",
"zpm": "zapotekiya mixtepec",
"wky": "wangkayutyuruyî",
"laq": "qabiaoyî",
"nja": "nzanyî",
"dmo": "kemezungî",
"dro": "daro-matu melanauyî",
"ato": "atong (Kamerun)",
"yif": "açeyî",
"zts": "zapotekiya tilquiapan",
"lah": "lahndayî",
"iry": "îrayayî",
"roa-bbn": "bourbonnais-berriçonî",
"aal": "afadeyî",
"czn": "zenzontepekiya çatîno",
"mpl": "watutiya navîn",
"lmg": "lamogayî",
"nlg": "gelayî",
"ayo": "ayoreoyî",
"zoh": "zoqueyiya çimalapa",
"mtp": "wiçi lhamtes noctenî",
"suv": "sulungî",
"xmt": "matbatî",
"zga": "kingayî",
"jbt": "jabutiyî",
"gof": "gofayî",
"rmt": "domarî",
"bwu": "bulî (Gana)",
"jod": "wojenakayî",
"yyu": "torricelli yauyî",
"sbf": "şaboyî",
"yan": "mayangnayî",
"kii": "kitsaiyî",
"trc": "trîkuyiya copala",
"shx": "şeyî",
"pgu": "paguyî",
"gwa": "mbatoyî",
"dyo": "jola-fonyiyî",
"yas": "gunuyî",
"igb": "ebirayî",
"plz": "paluanî",
"alv-gwa": "gwarayî",
"eke": "ekitî",
"cfd": "carayî",
"abb": "bankonî",
"amc": "amahuaca",
"rhp": "yahangî",
"lmb": "mereiyî",
"koa": "konomalayî",
"hlb": "halbiyî",
"eme": "emerillonî",
"nai-cig": "ciguayoyî",
"ael": "ambeleyî",
"jay": "yan-nhanguyî",
"tak": "talayî",
"mmy": "migaamayî",
"lmd": "lumunî",
"kfp": "korwayî",
"woi": "kamangî",
"akg": "anakalunguyî",
"nai-per": "pericú",
"nrl": "ngarlumayî",
"mew": "maakayî",
"hig": "kamweyî",
"hnd": "hindkoyiya başûrî",
"cuv": "cuvokî",
"ylg": "yeloguyî",
"une": "unemeyî",
"tpj": "tapieteyî",
"sos": "seenkuyî",
"nsh": "ngoşieyî",
"mit": "mixteciya pueblaya başûr",
"mxx": "mahouyî",
"bzk": "kreyoliya nîkaraguayî",
"esq": "esselenî",
"nbc": "çangî",
"anu": "anuak",
"snu": "senggî",
"mpu": "makurapî",
"hbb": "hubayî",
"cbg": "çimilayî",
"wti": "bertayî",
"app": "apma",
"spg": "sianî",
"snf": "noonî",
"mkp": "moikodî",
"viv": "idunayî",
"gem-pro": "proto-cermenî",
"omx": "moniya kevn",
"maw": "mamprulî",
"are": "arrernteya rojavayî",
"zah": "zangwalî",
"ssl": "sisaalayiya rojavayî",
"mpc": "mangarayî",
"qun": "kuynaultî",
"gyd": "kayardildî",
"ikx": "îkî",
"dij": "daiyî",
"bwq": "boboyiya başûrî",
"aum": "asu",
"axb": "abipon",
"zac": "zapotekiya ocotlan",
"xwk": "wangkumarayî",
"nzi": "nzimayî",
"mzv": "manzayî",
"kwe": "kwerbayî",
"sjk": "kemi samî",
"gmv": "gamoyî",
"bkt": "bolokî",
"atw": "atsugewî",
"zpk": "zapotekiya tlacolulita",
"wbe": "waritaî",
"ray": "rapayî",
"ojs": "ojibweyiya severnê",
"zmd": "maridanî",
"kxz": "kerewoyî",
"irr": "îrî",
"gbr": "gbagyiyî",
"bzx": "bozoyiya hainyaxo",
"aya": "awar",
"res": "reşeyî",
"oin": "oneyiya inebu",
"mwe": "mwerayî",
"ite": "îteneyî",
"gaf": "gendeyî",
"gdl": "diraşayî",
"nfd": "ahwayî",
"tlm": "tolomakoyî",
"mgv": "matengoyî",
"gqn": "kinikinaoyî",
"jah": "jah hutî",
"gog": "gogoyî",
"doy": "dompoyî",
"bfj": "bafanjî",
"xwc": "wokkonî",
"tei": "torricellî",
"sha": "şall-zwallî",
"stn": "owayî",
"emy": "mayaniya epigrafîk",
"azd": "durango nahuatliya rojavayî",
"bls": "balaesang",
"dul": "alabat island agtayî",
"xan": "xamtangayî",
"plk": "şinayiya kohistanî",
"sai-sac": "sacatayî",
"apf": "pahanan agta",
"npa": "nar phuyî",
"mfn": "mbembeyiya çema crossê",
"czk": "knaanicî",
"jow": "jowuluyî",
"cbb": "cabiyariyî",
"bpq": "banda malayî",
"sit-tsh": "tşobdunî",
"sbe": "salibayî (Gîneya nû)",
"mfj": "mefeleyî",
"lok": "lokoyî",
"kdu": "kadaruyî",
"ekp": "ekpeyeyî",
"bap": "bantawa",
"ali": "amaimonî",
"ynu": "yahunayî",
"tuf": "tuneboyiya navendî",
"ppu": "paporayî",
"dcr": "negerhollandsî",
"meq": "mereyî",
"nai-knm": "konomihuyî",
"ijn": "kalabariyî",
"hla": "haliayî",
"alm": "amblongî",
"ynn": "yanayî",
"ntj": "ngaanyatjarrayî",
"kbq": "kamanoyî",
"anm": "analî",
"ukg": "ukurigumayî",
"mav": "satere-maweyî",
"mfz": "mabaanî",
"kqq": "krenakî",
"hns": "hindustaniyiya karibî",
"taf": "tapirapeyî",
"mig": "mixteciya san miguel el grande",
"kni": "kanufiyî",
"sai-chp": "çapacurayî",
"cko": "anufoyî",
"wnu": "usanî",
"spe": "sepayî (Gîneya nû)",
"xrw": "karawayî",
"bnt-cmw": "çimwiinî",
"vam": "vanimoyî",
"sjr": "siar-lakî",
"poo": "pomoyiya navendî",
"mlz": "malaynonî",
"xks": "kumbewahayî",
"ldb": "idunî",
"tqb": "tembeyî",
"nai-sin": "sinacantanî",
"nur": "nuguriayî",
"mqx": "mamujuyî",
"unn": "kurnayî",
"bcn": "bîbaalî",
"ttr": "terayî",
"wkd": "moyî",
"kzl": "kayeliyî",
"ntk": "îkomayî",
"gdg": "ga'dangî",
"gbj": "bodo gadabayî",
"abi": "abîdjî",
"zao": "zapotekiya ozolotepec",
"sai-wnm": "wanhamî",
"thp": "thompsonî",
"cwe": "kwereyî",
"xbn": "kenaboî",
"gnq": "ganayî",
"tcc": "datoogayî",
"ztg": "zapotekiya xanaguia",
"zmj": "maridjabinî",
"lak": "lakayî",
"kvr": "kerînçî",
"ggu": "gbanî",
"anv": "denya",
"aof": "bragat",
"awr": "awera",
"zhb": "zhabayî",
"www": "wawayî",
"stb": "subaneniya bakurî",
"ret": "rettayî",
"oon": "öngeyî",
"mlv": "mwotlapî",
"mzr": "marúboyî",
"lag": "langiyî",
"kxc": "khonsoyî",
"itx": "îtikî",
"bja": "budza",
"ayu": "ayuyî",
"tba": "aikanayî",
"txm": "tominî",
"orx": "oroyî",
"nhq": "nahwatliya huaxcaleca",
"mpq": "matisî",
"kfk": "kinnauriyî",
"kzh": "dongolawiyî",
"bck": "bunabayî",
"ksf": "bafiayî"
},
"numberLabels": {
"y": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"s": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"d": {
"cat": "dualia tantum",
"display": "\u003Cabbr title=\"hejmar dual\"\u003Edu\u003C/abbr\u003E"
},
"p": {
"cat": "POS pirjimar",
"display": "\u003Cabbr title=\"pirjimar\"\u003Ep\u003C/abbr\u003E"
},
"p+": {
"cat": "POS axlebî pirjimar",
"display": "axlebî pirjimar"
},
"tp": {
"cat": "POS tenê pirjimar",
"display": "tenê pirjimar"
},
"j": {
"cat": "POS jimarbar",
"display": "[[jimarbar]]"
},
"j+": {
"cat": "POS axlebî jimarbar",
"display": "axlebî jimarbar"
},
"j/nj": {
"cat": "POS jimarbar û nejimarbar",
"display": "[[jimarbar]] û [[nejimarbar]]"
},
"nj": {
"cat": "POS nejimarbar",
"display": "[[nejimarbar]]"
},
"nj+": {
"cat": "POS axlebî nejimarbar",
"display": "axlebî [[nejimarbar]]"
},
"hj!": {
"display": "hejmar nepeyitandî ye"
},
"hj?": {
"req": true,
"display": "hejmar hewce ye"
}
}
}
4is2h0cjoo5psh9tpxrgynw4pi57fgt
6334276
6334227
2026-08-16T23:36:59Z
Wikihez
7344
6334276
json
application/json
{
"langChoices": {
"ku": "kurmancî",
"zza": "zazakî",
"ckb": "soranî",
"sdh": "kurdiya başûrî",
"hac": "hewramî",
"de": "almanî",
"ar": "erebî",
"fa": "farisî",
"fr": "fransî",
"fi": "fînî",
"en": "îngilîzî",
"ja": "japonî",
"sv": "swêdî",
"tr": "tirkî"
},
"posChoices": {
"navdêr": "Navdêr (îsm)",
"lêker": "Lêker (fiîl)",
"rengdêr": "Rengdêr (sifet)",
"hoker": "Hoker (zerf)",
"serenav": "Serenav (îsmê xisûsî)",
"artîkel": "Artîkel",
"baneşan": "Baneşan (nîda)",
"bazinedaçek": "Bazinedaçek",
"cînav": "Cînav (zemîr)",
"daçek": "Daçek (edat)",
"girêdek": "Girêdek",
"gotineke pêşiyan": "Gotineke pêşiyan",
"hejmar": "Hejmar",
"hevok": "Hevok (cimle)",
"îfade": "Îfade",
"kurtenav": "Kurtenav",
"navgir": "Navgir",
"paşdaçek": "Paşdaçek",
"paşgir": "Paşgir",
"pêşdaçek": "Pêşdaçek",
"pêşgir": "Pêşgir",
"pirtik": "Pirtik",
"reh": "Reh",
"sembol": "Sembol",
"tîp": "Tîp (herf)"
},
"posExamples": {
"navdêr": "Kelîmeyên ji bo tiştan (sêv, heywan, qedeh) yan terman (dilşadî, bextiyarî hwd.) di rêzimanê de.",
"lêker": "Kelîmeyên ji bo kirin an bûyînê wekî 'nivîsîn' yan 'xeber dan'.",
"rengdêr": "Kelîmeyên ku wesfa navdêran nîşan didin wekî 'sêva sor' yan 'xwarina şîrîn'.",
"hoker": "Kelîmeyên ku dem, cih yan awayê kirinê diyar dikin wekî 'bi vî awayî' yan 'demkî'.",
"daçek": "Kelîmeyên ku cih, dem yan hereketê diyar dikin wekî 'di.. de' yan 'ji'.",
"cînav": "Kelîmeyên ku li şûna navdêran tên bikaranîn daku navdêr neyên dubarekirin wekî 'ew' yan 'em'.",
"baneşan": "Kelîmeyên ku hest, deng hwd. îfade dikin wekî 'noşî!' yan 'aferîn'.",
"serenav": "Navdêrên ku şexsiyetên ferdî nîşan didin, wek navên kes, cih an organîzasyonan nimûne 'London' yan 'Diyarbekir'.",
"gotineke pêşiyan": "Gotinên ku di nav xelkê de bi berbelavî tên bikaranîn bo nimûne 'Serê neêşe ne hewceyî destmalê ye'."
},
"genderLabels": {
"m": "mê",
"n": "nêr",
"nt": "nêtar",
"mn": "mê yan nêr",
"mnmane": "mê yan nêr li gorî maneyê",
"mndudil": "mê yan nêr bikaranîn dudil e",
"g": "giştî",
"y": "yekjimar",
"p": "pirjimar"
},
"tplDefs": {
"rastnivîs": {
"langs": [
"ku"
],
"noLang": true
},
"ku-Latn": {
"langs": [
"ku"
],
"noLang": true
},
"ku-tewîna-nav": {
"langs": [
"ku"
],
"noLang": true
},
"sdh-Arab": {
"langs": [
"sdh"
],
"noLang": true
},
"hac-Arab": {
"langs": [
"hac"
],
"noLang": true
},
"ja-romanîzekirin": {
"langs": [
"ja"
],
"noLang": true
},
"ckb-Arab": {
"langs": [
"ckb"
],
"noLang": true
},
"zza-awayê-Jacobsonê": {
"langs": [
"zza"
]
},
"awayekî din": {
"langs": null
},
"guharto": {
"langs": null
},
"hevmaneya": {
"langs": null
},
"kevnbûyî": {
"langs": null
},
"kêmpeyda": {
"langs": null
},
"kurtkirin": {
"langs": null
},
"paşnav": {
"langs": null
},
"şaşnivîs": {
"langs": null
}
},
"jederDefs": {
"jêder-kitêb": {
"label": "Kitêb",
"fields": [
{
"key": "nivîskar",
"label": "Nivîskar"
},
{
"key": "sernav",
"label": "Sernav",
"multiline": true,
"width": "wide"
},
{
"key": "sernavê orjînal",
"label": "Sernavê orjînal",
"multiline": true,
"width": "wide"
},
{
"key": "terciman",
"label": "Terciman"
},
{
"key": "weşanxane",
"label": "Weşanxane"
},
{
"key": "cih",
"label": "Cih"
},
{
"key": "sal",
"label": "Sal",
"width": "narrow"
},
{
"key": "çap",
"label": "Çap",
"width": "narrow"
},
{
"key": "rûpel",
"label": "Rûpel",
"width": "narrow"
},
{
"key": "isbn",
"label": "ISBN",
"width": "narrow"
},
{
"key": "url",
"label": "URL",
"width": "wide"
}
]
},
"jêder-kovar": {
"label": "Kovar",
"fields": [
{
"key": "nivîskar",
"label": "Nivîskar"
},
{
"key": "nivîskar2",
"label": "Nivîskar 2"
},
{
"key": "sernav",
"label": "Sernav",
"multiline": true,
"width": "wide"
},
{
"key": "sernavê orjînal",
"label": "Sernavê orjînal",
"multiline": true,
"width": "wide"
},
{
"key": "kovar",
"label": "Kovar"
},
{
"key": "terciman",
"label": "Terciman"
},
{
"key": "weşanxane",
"label": "Weşanxane"
},
{
"key": "cild",
"label": "Cild",
"width": "narrow"
},
{
"key": "hejmar",
"label": "Hejmar",
"width": "narrow"
},
{
"key": "sal",
"label": "Sal",
"width": "narrow"
},
{
"key": "rûpel",
"label": "Rûpel",
"width": "narrow"
},
{
"key": "url",
"label": "URL",
"width": "wide"
}
]
},
"jêder-websîte": {
"label": "Websîte",
"fields": [
{
"key": "nivîskar",
"label": "Nivîskar"
},
{
"key": "sernav",
"label": "Sernav",
"multiline": true,
"width": "wide"
},
{
"key": "terciman",
"label": "Terciman"
},
{
"key": "websîte",
"label": "Websîte"
},
{
"key": "url",
"label": "URL",
"width": "wide"
},
{
"key": "arşîv",
"label": "Arşîva URLê",
"width": "wide"
},
{
"key": "sal",
"label": "Sal",
"width": "narrow"
},
{
"key": "tarîx",
"label": "Tarîx",
"width": "narrow"
}
]
}
},
"pronTpls": {
"ku-IPA": {
"desc": "IPAya otomatîk ji bo kurmancî (kontrol hewce ye)",
"snippet": "* {{ku-IPA}}",
"langs": [
"ku"
],
"noLang": true
},
"ku-kîte": {
"desc": "Hecekirina otomatîk ji bo kurmancî",
"snippet": "* {{ku-kîte}}",
"langs": [
"ku"
],
"noLang": true
},
"zza-IPA": {
"desc": "IPAya otomatîk ji bo zazakî",
"snippet": "* {{zza-IPA}}",
"langs": [
"zza"
],
"noLang": true
},
"zza-kîte": {
"desc": "Hecekirina otomatîk ji bo zazakî",
"snippet": "* {{zza-kîte}}",
"langs": [
"zza"
],
"noLang": true
},
"IPA": {
"desc": "Alfabeya fonetîk a navmiletî (IPA)",
"snippet": "* {{IPA|CODE|/…/}}"
},
"deng": {
"desc": "Nimûneya telafûzê bi dosyeya dengî",
"snippet": "* {{deng|CODE|navê_dosyayê.ogg|dever=}}"
},
"bilêv": {
"desc": "{{bilêv|herf|nerm, req|hejmar}}",
"snippet": "* {{bilêv|herf|cure|hejmar}}"
},
"kîte": {
"desc": "Şablona hecekirinê",
"snippet": "* {{kîte|CODE|he|ce}}"
},
"hevdeng": {
"desc": "Hevdeng — gotinên bi heman dengî",
"snippet": "* {{hevdeng|CODE|…}}"
}
},
"etymTpls": {
"ji": {
"desc": "Ji zimanekî din hatiye — {{ji|en|ku|anatomy|tr=|t=}}",
"snippet": "{{ji|SOURCELANG|CODE|…}}"
},
"deyn": {
"desc": "Deynkirî — {{deyn|lang|kod|kelîme}}",
"snippet": "{{deyn|SOURCELANG|CODE|…}}"
},
"mîr": {
"desc": "Mîrasmayî — {{mîr|lang|kod|kelîme}}",
"snippet": "{{mîr|SOURCELANG|CODE|…}}"
},
"qertaf": {
"desc": "Ji bi pêşgir, paşgir û hevedudanî — {{qertaf|kod|pêşgir-|reh}}",
"snippet": "{{qertaf|CODE|…|…}}"
},
"q": {
"desc": "Referans bo rûpelekê — {{q|kod|kelîme}} navê zimanî dernakeve",
"snippet": "{{q|CODE|…}}"
},
"hv": {
"desc": "Hevreh — {{hv|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{hv|SOURCELANG|…}}"
},
"nh": {
"desc": "Nehevreh — {{nh|lang|kelîme}} navê zimanî derdikeve",
"snippet": "{{nh|SOURCELANG|…}}"
},
"cotreh": {
"desc": "Cotreh — teror {{cotreh|ku|tirs}}",
"snippet": "{{cotreh|CODE|…}}"
},
"yekser": {
"desc": "tercimeya yekser — {{yekser|lang|kod|kelîme}}",
"snippet": "{{yekser|SOURCELANG|CODE|…}}"
},
"wergirtin": {
"desc": "Wergirtina maneyê — {{wergirtin|lang|kod|kelîme}}",
"snippet": "{{wergirtin|SOURCELANG|CODE|…}}"
},
"paşvesazî": {
"desc": "Paşvesazî — greed {{paşvesazî|en|greedy}}",
"snippet": "{{paşvesazî|CODE|…}}"
},
"qusandin": {
"desc": "Qusandin — Miho {{qusandin|ku|Mihemmed}}",
"snippet": "{{qusandin|CODE|…}}"
},
"têkilkirin": {
"desc": "Têkilkirin — sormancî {{têkilkirin|ku|soranî|kurmancî}}",
"snippet": "{{têkilkirin|CODE|…}}"
},
"nyz": {
"desc": "Nayê zanîn — {{nyz|ku|hûr=1}}",
"snippet": "{{nyz|CODE}}"
},
"onom": {
"desc": "Onomatopoetîk — {{onom|ku|hûr=1}}",
"snippet": "{{onom|CODE}}"
},
"navmiletî": {
"desc": "Navmiletî — {{navmiletî|ku|hûr=1}}",
"snippet": "{{navmiletî|CODE}}"
},
"neo": {
"desc": "neolojîzm — {{neolojîzm|ku|hûr=1}}",
"snippet": "{{neolojîzm|CODE}}"
}
},
"metadata": {
"aa": {
"haswikt": 1,
"sc": [
"Latn",
"Ethi"
]
},
"ab": {
"haswikt": 1,
"sc": [
"Cyrl",
"Latn",
"Geor"
]
},
"aer": {
"sc": "Latn"
},
"af": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ak": {
"haswikt": 1
},
"akk": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Xsux"
},
"als": {
"haswikt": 1
},
"am": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1,
"sc": "Ethi"
},
"an": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ang": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"ar": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"alt": 1,
"haswikt": 1,
"sc": "Arab"
},
"arc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": [
"Hebr",
"Syrc"
]
},
"are": {},
"arz": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"sc": "Arab"
},
"as": {
"haswikt": 1,
"sc": "Beng"
},
"ast": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"av": {
"haswikt": 1
},
"ay": {
"haswikt": 1
},
"az": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl",
"Arab"
]
},
"ba": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"bar": {},
"bat-smg": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"bbl": {
"sc": "Geor"
},
"be": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"be-x-old": {
"sc": "Cyrl"
},
"bg": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"bh": {
"haswikt": 1,
"sc": "Deva"
},
"bhb": {
"sc": "Deva"
},
"bi": {
"haswikt": 1
},
"blt": {
"sc": "Tavt"
},
"bm": {
"haswikt": 1,
"sc": [
"Latn",
"Nkoo",
"Arab"
]
},
"bn": {
"haswikt": 1,
"sc": "Beng"
},
"bo": {
"haswikt": 1,
"sc": "Tibt"
},
"br": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ca": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"cdo": {
"sc": "Hani"
},
"ce": {
"sc": "Cyrl"
},
"ch": {
"haswikt": 1
},
"chr": {
"haswikt": 1,
"sc": "Cher"
},
"cjy": {
"sc": "Hani"
},
"ckb": {
"haswikt": 1,
"sc": "Arab",
"wsc": "ku-Arab"
},
"cmn": {
"sc": "Hani",
"wiktprefix": "zh"
},
"co": {
"haswikt": 1
},
"cpx": {
"sc": "Hani"
},
"cr": {
"haswikt": 1,
"sc": "Cans"
},
"crh": {},
"cs": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"csb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"cu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": [
"Cyrs",
"Glag"
]
},
"cv": {
"sc": "Cyrl"
},
"cy": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"czh": {
"sc": "Hani"
},
"czo": {
"sc": "Hani"
},
"da": {
"g": [
"g",
"nt",
"g-p",
"nt-p",
"p"
],
"haswikt": 1
},
"dax": {},
"de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"dhg": {},
"djb": {},
"dji": {},
"djr": {},
"dng": {
"sc": "Cyrl"
},
"dsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"dsx": {},
"duj": {},
"dv": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Thaa"
},
"dz": {
"haswikt": 1,
"sc": "Tibt"
},
"el": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"en": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"eo": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"es": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"et": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"ett": {
"g": [
"y",
"p"
],
"sc": "Ital"
},
"eu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fa": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-cls": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fa-ira": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"fi": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"fil": {},
"fj": {
"haswikt": 1
},
"fo": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1
},
"fr": {
"g": [
"n",
"m",
"mnmane",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"frm": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fro": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fur": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"fy": {
"g": [
"g",
"nt",
"p"
],
"haswikt": 1
},
"ga": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"gan": {
"sc": "Hani"
},
"gd": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gez": {
"sc": "Ethi"
},
"gl": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"gmw-ecg": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gmy": {
"sc": "Linb"
},
"gn": {
"haswikt": 1
},
"gnn": {
"sc": "Latn"
},
"got": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"sc": "Goth"
},
"grc": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
]
},
"gsw": {
"g": [
"n",
"m",
"nt",
"p"
],
"allowCaps": 1
},
"gu": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Gujr"
},
"guf": {
"sc": "Latn"
},
"gv": {
"haswikt": 1
},
"ha": {
"haswikt": 1
},
"hak": {
"sc": "Hani"
},
"har": {
"sc": "Ethi"
},
"he": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"hi": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Deva"
},
"hif": {
"sc": [
"Latn",
"Deva"
]
},
"hit": {
"sc": "Xsux"
},
"hrx": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"hsb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"hsn": {
"sc": "Hani"
},
"hu": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"hy": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Armn"
},
"ia": {
"haswikt": 1
},
"id": {
"haswikt": 1
},
"ie": {
"haswikt": 1
},
"ik": {
"haswikt": 1
},
"ike": {
"sc": "Cans"
},
"ikt": {
"sc": "Cans"
},
"io": {
"haswikt": 1
},
"is": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"it": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"iu": {
"haswikt": 1,
"sc": "Cans"
},
"ja": {
"haswikt": 1,
"sc": "Jpan"
},
"jay": {
"sc": "Latn"
},
"jbo": {
"haswikt": 1
},
"jv": {
"haswikt": 1
},
"ka": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"khb": {
"sc": [
"Talu",
"Lana",
"Thai"
]
},
"kjh": {
"sc": "Cyrl"
},
"kk": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"kkh": {
"sc": [
"Lana",
"Thai"
]
},
"kl": {
"haswikt": 1
},
"km": {
"haswikt": 1,
"sc": "Khmr"
},
"kn": {
"haswikt": 1,
"sc": "Knda"
},
"ku": {
"g": [
"n",
"m",
"mnmane",
"mndudil",
"n-p",
"m-p",
"p"
],
"wiktprefix": "ku"
},
"ko": {
"haswikt": 1,
"sc": "Kore"
},
"krc": {
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"ks": {
"haswikt": 1,
"sc": [
"Arab",
"Deva"
],
"wsc": "ks-Arab"
},
"kw": {
"haswikt": 1
},
"ky": {
"haswikt": 1,
"g": [
"y",
"p"
],
"sc": "Cyrl"
},
"la": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"lb": {
"g": [
"n",
"m",
"nt",
"p"
],
"haswikt": 1,
"allowCaps": 1
},
"lez": {
"sc": "Cyrl"
},
"li": {
"haswikt": 1
},
"lki": {
"wiktprefix": "ku"
},
"lld": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lmo": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"ln": {
"haswikt": 1
},
"lo": {
"haswikt": 1,
"sc": "Laoo"
},
"lt": {
"alt": 1,
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lv": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"lzz": {
"sc": "Geor"
},
"mg": {
"haswikt": 1
},
"mh": {
"haswikt": 1
},
"mi": {
"g": 0,
"haswikt": 1
},
"mk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ml": {
"haswikt": 1,
"sc": "Mlym"
},
"mn": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": [
"Cyrl",
"Mong"
]
},
"mnp": {
"sc": "Hani"
},
"mo": {
"haswikt": 1,
"sc": "Cyrl"
},
"mol": {
"sc": "Cyrl"
},
"mr": {
"g": [
"n",
"m",
"nt"
],
"haswikt": 1,
"sc": "Deva"
},
"ms": {
"haswikt": 1,
"sc": [
"Latn",
"Arab"
]
},
"mt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"mwp": {
"sc": "Latn"
},
"my": {
"haswikt": 1,
"sc": "Mymr"
},
"na": {
"haswikt": 1
},
"nah": {
"haswikt": 1
},
"nan": {
"wiktprefix": "zh-min-nan",
"sc": "Hani"
},
"nb": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"wiktprefix": "no"
},
"nds-de": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"nds-nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"ne": {
"haswikt": 1,
"sc": "Deva"
},
"nl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nn": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"no": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1
},
"nod": {
"sc": "Lana"
},
"non": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"oc": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"om": {
"haswikt": 1
},
"or": {
"haswikt": 1,
"sc": "Orya"
},
"orv": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrs"
},
"osc": {
"sc": "Ital"
},
"ota": {
"g": [
"y",
"p"
],
"sc": "Arab",
"wsc": "ota-Arab"
},
"pa": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": [
"Guru",
"Arab"
]
},
"pdt": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "nds"
},
"peo": {
"sc": "Xpeo"
},
"phn": {
"sc": "Phnx"
},
"pi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1
},
"pjt": {
"sc": "Latn"
},
"pl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"pms": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"pox": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"împf",
"pf"
]
},
"prs": {
"wiktprefix": "fa",
"sc": "Arab",
"wsc": "fa-Arab"
},
"ps": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ps-Arab"
},
"pt": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1
},
"qu": {
"haswikt": 1
},
"rgn": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"rit": {
"sc": "Latn"
},
"rm": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"rn": {
"haswikt": 1
},
"ro": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"roa-rup": {
"haswikt": 1
},
"ru": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"rue": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"sc": "Cyrl"
},
"ruo": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rup": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"wiktprefix": "roa-rup"
},
"ruq": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"rw": {
"haswikt": 1
},
"sa": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
],
"haswikt": 1,
"sc": "Deva"
},
"sah": {
"sc": "Cyrl"
},
"sc": {
"haswikt": 1
},
"scn": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"sco": {
"sc": "Latn"
},
"sd": {
"haswikt": 1,
"sc": "Arab",
"wsc": "sd-Arab"
},
"sdh": {
"sc": "Arab",
"wiktprefix": "ku"
},
"sg": {
"haswikt": 1
},
"sh": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": [
"Latn",
"Cyrl"
]
},
"si": {
"haswikt": 1,
"sc": "Sinh"
},
"simple": {
"haswikt": 1
},
"sk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sl": {
"alt": 1,
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1
},
"sm": {
"haswikt": 1
},
"sn": {
"haswikt": 1
},
"so": {
"haswikt": 1
},
"spx": {
"sc": "Ital"
},
"sq": {
"g": [
"n",
"m"
],
"haswikt": 1
},
"ss": {
"haswikt": 1
},
"st": {
"haswikt": 1
},
"su": {
"haswikt": 1
},
"sux": {
"sc": "Xsux"
},
"sv": {
"g": [
"g",
"nt",
"g-p",
"nt-p"
],
"haswikt": 1
},
"sva": {
"sc": "Geor"
},
"sw": {
"nclass": 1,
"haswikt": 1
},
"syc": {
"sc": "Syrc"
},
"syr": {
"sc": "Syrc"
},
"szl": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
]
},
"ta": {
"haswikt": 1,
"sc": "Taml"
},
"tdd": {
"sc": "Tale"
},
"te": {
"haswikt": 1,
"sc": "Telu"
},
"tg": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Cyrl"
},
"th": {
"haswikt": 1,
"sc": "Thai"
},
"ti": {
"haswikt": 1,
"sc": "Ethi"
},
"tig": {
"sc": "Ethi"
},
"tiw": {
"sc": "Latn"
},
"tk": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tl": {
"haswikt": 1,
"sc": [
"Latn",
"Tglg"
]
},
"tmr": {
"sc": "Hebr"
},
"tn": {
"haswikt": 1
},
"to": {
"haswikt": 1
},
"tpi": {
"haswikt": 1
},
"tr": {
"g": [
"y",
"p"
],
"alt": 1,
"haswikt": 1
},
"ts": {
"haswikt": 1
},
"tt": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"tts": {
"sc": "Thai"
},
"tw": {
"haswikt": 1
},
"udi": {
"sc": [
"Cyrl",
"Latn",
"Armn",
"Geor"
]
},
"ug": {
"g": [
"y",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ug-Arab"
},
"uga": {
"sc": "Ugar"
},
"uk": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p",
"împf",
"pf"
],
"haswikt": 1,
"sc": "Cyrl"
},
"ulk": {
"sc": "Latn"
},
"ur": {
"g": [
"n",
"m",
"n-p",
"m-p",
"p"
],
"haswikt": 1,
"sc": "Arab",
"wsc": "ur-Arab"
},
"uz": {
"g": [
"y",
"p"
],
"haswikt": 1
},
"vec": {
"g": [
"n",
"m",
"n-p",
"m-p"
],
"haswikt": 1
},
"vi": {
"haswikt": 1
},
"vls": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p"
]
},
"vo": {
"haswikt": 1
},
"wa": {
"haswikt": 1
},
"wbp": {
"sc": "Latn"
},
"wo": {
"haswikt": 1
},
"wuu": {
"sc": "Hani"
},
"xae": {
"sc": "Ital"
},
"xcr": {
"sc": "Cari"
},
"xfa": {
"sc": "Ital"
},
"xh": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"xlc": {
"sc": "Lyci"
},
"xld": {
"sc": "Lydi"
},
"xlu": {
"sc": "Xsux"
},
"xmf": {
"sc": "Geor"
},
"xno": {
"g": [
"n",
"m",
"n-p",
"m-p"
]
},
"xrr": {
"sc": "Ital"
},
"xst": {
"sc": "Ethi"
},
"xum": {
"sc": "Ital"
},
"xve": {
"sc": "Ital"
},
"xvo": {
"sc": "Ital"
},
"yi": {
"g": [
"n",
"m",
"nt",
"n-p",
"m-p",
"nt-p",
"p"
],
"haswikt": 1,
"sc": "Hebr"
},
"yo": {
"haswikt": 1
},
"yua": {
"g": [
"y",
"p"
],
"alt": 1
},
"yue": {
"sc": "Hani"
},
"za": {
"haswikt": 1,
"sc": "Latn"
},
"zh-classical": {
"sc": "Hani"
},
"zh-min-nan": {
"haswikt": 1
},
"zh-yue": {
"sc": "Hani"
},
"zu": {
"alt": 1,
"nclass": 1,
"haswikt": 1
},
"zza": {
"g": [
"n",
"m",
"mn",
"n-p",
"m-p",
"p"
],
"wiktprefix": "diq"
}
},
"ziman": {
"ku": "kurmancî",
"en": "îngilîzî",
"tr": "tirkî",
"de": "almanî",
"pl": "polonî",
"ja": "japonî",
"fa": "farisî",
"sh": "sirboxirwatî",
"fr": "fransî",
"ka": "gurcî",
"it": "îtalî",
"fi": "fînî",
"ru": "rusî",
"es": "spanî",
"ar": "erebî",
"nl": "holendî",
"hi": "hindî",
"cs": "çekî",
"cmn": "mandarînî",
"pt": "portugalî",
"uk": "ûkraynî",
"hu": "mecarî",
"ko": "koreyî",
"be": "belarusî",
"la": "latînî",
"ckb": "soranî",
"sv": "swêdî",
"hy": "ermenî",
"bg": "bulgarî",
"ro": "romanyayî",
"da": "danmarkî",
"gl": "galîsî",
"el": "yûnanî",
"eo": "esperantoyî",
"tl": "tagalogî",
"fo": "ferî",
"vi": "viyetnamî",
"ca": "katalanî",
"is": "îzlendî",
"et": "estonî",
"az": "azerî",
"ceb": "sebwanoyî",
"mk": "makedonî",
"he": "îbranî",
"th": "tayî",
"lv": "latviyayî",
"sk": "slovakî",
"ms": "malezî",
"ga": "îrlendî",
"tg": "tacikî",
"grc": "yûnaniya kevn",
"sl": "slovenî",
"kk": "qazaxî",
"mi": "maorî",
"mn": "mongolî",
"sq": "albanî",
"uz": "ozbekî",
"lt": "lîtwanî",
"ur": "urdûyî",
"km": "ximêrî",
"id": "endonezyayî",
"cy": "weylsî",
"lo": "lawsî",
"bn": "bengalî",
"zza": "zazakî",
"ky": "kirgizî",
"yi": "yidîşî",
"gd": "gaelîka skotî",
"my": "burmayî",
"te": "telûgûyî",
"nb": "norweciya bokmålî",
"zh": "çînî",
"ps": "peştûyî",
"eu": "baskî",
"oc": "oksîtanî",
"sw": "swahîlî",
"ast": "astûrî",
"af": "afrîkansî",
"tk": "tirkmenî",
"nn": "norweciya nînorskî",
"yue": "kantonî",
"ug": "oygurî",
"rm": "romancî",
"sc": "sardînî",
"vo": "volapûkî",
"io": "îdoyî",
"ml": "malayalamî",
"mt": "maltayî",
"ba": "başkîrî",
"ta": "tamîlî",
"ang": "îngilîziya kevn",
"sa": "sanskrîtî",
"tt": "teterî",
"rup": "aromanî",
"nrf": "normandî",
"no": "norwecî",
"scn": "sicîlî",
"br": "bretonî",
"as": "asamî",
"bo": "tîbetî",
"nan": "minnanî",
"nv": "navajoyî",
"fy": "frîsî",
"pa": "puncabî",
"vec": "venîsî",
"fur": "friyolî",
"wa": "walonî",
"haw": "hawayî",
"lb": "luksembûrgî",
"am": "amharî",
"arz": "erebiya misrî",
"ia": "înterlîngua",
"kl": "kalalîsûtî",
"mr": "maratî",
"gv": "manksî",
"qu": "keçwayî",
"dsb": "sorbiya jêrîn",
"si": "kîngalî",
"kn": "kannadayî",
"sdh": "kurdiya başûrî",
"ota": "osmanî",
"gu": "gujaratî",
"ce": "çeçenî",
"jv": "javayî",
"pdt": "plodîşî",
"chr": "çerokî",
"nds": "nedersaksî",
"acw": "erebiya hîcazî",
"cu": "slaviya kevn",
"ne": "nepalî",
"kw": "kornî",
"hsb": "sorbiya jorîn",
"za": "zhuangî",
"os": "osetî",
"se": "samiya bakurî",
"dng": "dunganî",
"izh": "îngriyî",
"sco": "skotî",
"got": "gotî",
"or": "oriyayî",
"mnc": "mançûyî",
"crh": "teteriya krîmî",
"ltg": "latgalî",
"sah": "yakutî",
"ht": "haîtî",
"ary": "erebiya fasî",
"ha": "hawsayî",
"zu": "zuluyî",
"cv": "çuvaşî",
"yo": "yorubayî",
"ab": "abxazî",
"so": "somalî",
"frr": "frîsiya bakur",
"lad": "ladînoyî",
"an": "aragonî",
"ti": "tigrînî",
"aii": "aramiya nû ya asûrî",
"hak": "hakkayî",
"enm": "îngilîziya navîn",
"pi": "palî",
"navz": "navzimanî",
"ny": "çîçewayî",
"om": "oromoyî",
"nds-de": "almaniya jêrîn a almanî",
"lmo": "lombardî",
"csb": "kaşûbî",
"alt": "altayiya başûrî",
"arc": "aramî",
"bcl": "bîkoliya naverast",
"co": "korsîkayî",
"oj": "ojibweyî",
"pap": "papyamentoyî",
"rue": "rusînî",
"av": "avarî",
"txb": "toxarî B",
"xal": "kalmîkî",
"cop": "qiptî",
"mg": "malagasî",
"nap": "napolîtanî",
"gsw": "elmanîşî",
"dlm": "dalmatî",
"gn": "guwaranî",
"orv": "slaviya rojavayî ya kevn",
"tyv": "tuvanî",
"dv": "divehî",
"ady": "adîgeyî",
"bua": "buryatî",
"wuu": "wuyî",
"pam": "kapampanganî",
"myv": "erzayî",
"udm": "udmurtî",
"khb": "lueyî",
"rom": "romanî",
"su": "sundanî",
"ilo": "îlokanoyî",
"syc": "siryaniya klasîk",
"zdj": "şîngazîcayî",
"cdo": "min dongî",
"evn": "evenkî",
"kum": "kumikî",
"bal": "belûçî",
"lld": "ladînî",
"inh": "îngûşî",
"sd": "sindhî",
"lez": "lezgînî",
"fro": "fransiya kevn",
"non": "norsiya kevn",
"liv": "lîvonî",
"tet": "tetûmî",
"tpi": "tokpisinî",
"shn": "şanî",
"wo": "wolofî",
"mrw": "maranaoyî",
"gld": "nanayî",
"nah": "nahwatlî",
"ki": "kîkûyûyî",
"st": "sotoyiya başûr",
"mww": "hmongî",
"lij": "lîgûrî",
"rgn": "romagnolî",
"li": "lîmbûrgî",
"sm": "samoayî",
"sga": "îrlendiya kevn",
"ks": "keşmîrî",
"pms": "piedmontîsî",
"ccc": "çamicuroyî",
"hil": "hîlîgaynonî",
"xmf": "megrelî",
"hrx": "hunsrikî",
"rif": "tarifitî",
"krc": "karaçay-balkarî",
"war": "warayî",
"mwl": "mîrandî",
"syl": "sîlhetî",
"ee": "eweyî",
"ln": "lingalayî",
"pdc": "almaniya Pennsylvania",
"goh": "almaniya bilind a kevn",
"tly": "talişî",
"nci": "nahwatliya klasîk",
"ckt": "çukçî",
"ain": "aynuyî",
"uga": "ugarîtî",
"xh": "xosayî",
"sms": "samiya skoltî",
"kea": "kapverdî",
"ajp": "erebiya başûrê şamê",
"kjh": "xakasî",
"iu": "înuîtî",
"dz": "conxayî",
"srn": "sirananî",
"vro": "voroyî",
"mdf": "mokşayî",
"xcl": "ermeniya kevn",
"mhr": "mariya rojhilatî",
"cic": "çîkasawî",
"esu": "yupîkiya navendî",
"cjs": "şorî",
"wym": "wîlamowî",
"apw": "apaçiya rojava",
"mba": "higaononî",
"kaw": "javayiya kevn",
"ovd": "elfdalî",
"to": "tongî",
"ist": "îstriyotî",
"szl": "silesî",
"yua": "mayayiya yukatekî",
"tsg": "tausugî",
"zea": "zêlandî",
"bar": "bayerî",
"luy": "luhyayî",
"mzn": "mazenderanî",
"mhn": "mòçenoyî",
"kxd": "bruneyî",
"lzz": "lazî",
"kbd": "kabardî",
"afb": "erebiya kendavî",
"krl": "karelî",
"eve": "evenî",
"stq": "frîsiya saterlandî",
"ak": "akanî",
"tab": "tabasaranî",
"sat": "santalî",
"fj": "fîjî",
"sva": "svanî",
"egy": "misrî",
"ig": "îgboyî",
"hac": "hewramî",
"ay": "aymarayî",
"gur": "farefareyî",
"rw": "kînyarwandayî",
"twf": "taosî",
"koi": "komî-permyakî",
"lkt": "lakotayî",
"akl": "aklanon",
"yai": "yagnobî",
"ryu": "okînawanî",
"rhg": "rohingyayî",
"vep": "vepsî",
"smn": "samiya înarî",
"egl": "emilî",
"ty": "tahîtî",
"mdh": "maguindanaoyî",
"gag": "gagawzî",
"tzm": "temazîxtiya atlasa navendî",
"sgs": "samogîtî",
"ff": "fulahî",
"lou": "kreyoliya fransî ya Louisiana",
"mel": "melanauyiya navendî",
"niv": "nîvxî",
"aa": "afarî",
"gez": "etiyopiya klasîk",
"ext": "extremaduranî",
"frp": "frankoprovansî",
"kpv": "komî-zîriyî",
"udi": "udî",
"sma": "samiya başûr",
"nog": "nogayî",
"akk": "akadî",
"lbe": "lakî",
"min": "mînangkabawî",
"kg": "kîkongoyî",
"ban": "balînî",
"dlg": "dolganî",
"ace": "açehî",
"pox": "polabî",
"ppl": "pîpîlî",
"mnz": "monî",
"kaa": "karakalpakî",
"kab": "kabîlî",
"vot": "votî",
"luo": "luoyî",
"sn": "şonayî",
"swb": "komorî",
"roa-tar": "tarantinoyî",
"lg": "lugandayî",
"nds-nl": "saksoniya jêrîn ya holendî",
"dtp": "dusuniya navendî",
"shi": "taşelhitî",
"msk": "mansakayî",
"rap": "rapanuyî",
"mer": "meruyî",
"ik": "înupîakî",
"prg": "prusiya kevn",
"chk": "çûkî",
"kam": "kambayî",
"chy": "çeyenî",
"nod": "tayiya bakurî",
"akz": "alabamayî",
"xag": "aghwanî",
"ase": "zimanê destan ê emerîkî",
"nmn": "ǃxóõ",
"gun": "mbya guaraniyî",
"cr": "krî",
"yag": "yamanayî",
"otk": "tirkiya kevn",
"sid": "sidamoyî",
"tum": "tumbukayî",
"pjt": "pîttcantcatcarayî",
"sux": "sûmerî",
"bbl": "batsbî",
"jje": "jejuyî",
"bm": "bambarayî",
"frm": "fransiya navîn",
"tpn": "tupînambayî",
"pal": "farisiya navîn",
"bi": "bîslamayî",
"ksw": "s'gaw karenî",
"ssy": "sahoyî",
"bdr": "bajwayiya kendava rojava",
"sne": "bau bidayuh",
"sjd": "samiya kildînî",
"na": "nawrûyî",
"pag": "pangasînanî",
"abq": "abazayî",
"tn": "tswanayî",
"osx": "saksoniya kevn",
"aqc": "arçî",
"xsr": "şerpayî",
"kok": "konkanî",
"kln": "kalencînî",
"win": "wînnebagoyî",
"dar": "dargwayî",
"mfe": "kreyoliya morîtanî",
"iba": "îbanî",
"ae": "avestayî",
"mos": "moreyî",
"arn": "mapuçî",
"ill": "îranunî",
"mrj": "mariya rojavayî",
"cst": "ohloniya bakur",
"blt": "tay dam",
"apc": "erebiya bakûrê şamê",
"gan": "ganî",
"ii": "yiyiya siçuwayî",
"xto": "toxarî A",
"wmw": "mwanî",
"avd": "elwîrî-wîderî",
"pro": "oksîtaniya kevn",
"pcc": "buyî",
"jra": "jarayî",
"new": "newarî",
"unm": "unamî",
"mic": "mîkmakî",
"cjm": "çamiya rojhilatî",
"sdn": "galûrîsî",
"lus": "mizoyî",
"cjy": "jinî",
"tli": "tlingitî",
"bug": "bugî",
"pcd": "pîkardî",
"hop": "hopîyî",
"ofs": "frîsiya kevn",
"tft": "ternateyî",
"roa-opt": "portugaliya kevn",
"gil": "kîrîbatî",
"kac": "cingphoyî",
"csm": "sierra mîwokiya navendî",
"sdc": "sassarîsî",
"ood": "o'odhamî",
"hsn": "xiangî",
"ruo": "îstro-romanyayî",
"mbb": "bukidnon manoboyiya rojavayî",
"bho": "bojpûrî",
"ani": "andî",
"kgp": "kayingangî",
"ss": "swazî",
"mnp": "min beî",
"aho": "ahomî",
"moe": "montagnaîsî",
"naq": "namayî",
"mvi": "miyakoyî",
"duu": "drungî",
"lut": "luşûtsîdî",
"sce": "dongşîangî",
"bqi": "bextiyarî",
"mrc": "marîkopayî",
"gmh": "almaniya bilind a navîn",
"cim": "cimbrî",
"aer": "arrernteyiya rojavayî",
"agj": "argobbayî",
"ium": "îu mienî",
"tay": "atayalî",
"ale": "alêwîtî",
"str": "saanîçî",
"zku": "kaurnayî",
"pnt": "yûnaniya pontî",
"gmw-cfr": "frankoniya navendî",
"bxd": "pelayî",
"ems": "alutîqî",
"mtq": "muongî",
"hit": "hîtîtî",
"mak": "makasarî",
"bjn": "banjarî",
"nay": "ngarrindjerî",
"dak": "dakotayî",
"yrk": "nenetsiya tundra",
"pac": "pacohî",
"ch": "çamoroyî",
"vls": "flamiya rojava",
"acm": "erebiya iraqî",
"smj": "samiya lule",
"lmy": "lamboyayî",
"jam": "kreyoliya jameîkî",
"yka": "yakanî",
"gmw-ecg": "almaniya navendî ya rojhilat",
"xsv": "sûdovî",
"cdm": "çepangî",
"bdk": "buduxî",
"isd": "îsnagî",
"zgh": "amazîxiya fasî",
"css": "ohloniya başûr",
"kqr": "kîmaragangî",
"glk": "gîlekî",
"aeb": "erebiya tunusî",
"lki": "lekî",
"kdr": "karaîmî",
"kld": "gamîlaraayî",
"tdd": "tayî nüa",
"xqa": "karaxanîdî",
"nso": "sotoyiya bakur",
"crs": "kreyoliya seyşelî",
"mvv": "tagal murutî",
"kim": "tofalarî",
"swg": "swabî",
"pau": "palawî",
"wrh": "wiradhurî",
"cho": "çoktawî",
"tig": "tigreyî",
"ivv": "îvatanî",
"ddg": "fatalukuyî",
"rmf": "romaniya kalo fînî",
"pih": "pîtkarnî-norfolkî",
"phn": "fenîkî",
"ktu": "kitubayî",
"mwr": "marwarî",
"wbp": "warlpirî",
"zun": "zuniyî",
"crk": "kriya bejayî",
"ebk": "bontociya rojhilatî",
"nup": "nupeyî",
"yoi": "yonagunî",
"atj": "atîkamekwî",
"duo": "dupaningan agtayî",
"kca": "xantî",
"dta": "daurî",
"rar": "rarotongî",
"abe": "abenakî",
"aot": "atong (Hindistan)",
"osa": "osageyî",
"ami": "amîs",
"sea": "semaî",
"mh": "marşalî",
"mad": "madurayî",
"sei": "sêriyî",
"moh": "mohawkî",
"meu": "hîrî motuyî",
"pwn": "paîwanî",
"okm": "koreyiya navîn",
"xug": "kunigamî",
"amu": "amuzgoyî",
"ine-pro": "proto-hindûewropî",
"tzo": "tzotzîlî",
"ksc": "kalingayiya başûrî",
"sib": "sebopî",
"uum": "ûrûmî",
"kjj": "khinalugî",
"pis": "pijînî",
"peo": "farisiya kevn",
"sg": "sangoyî",
"tyz": "tàyî",
"kpy": "koryakî",
"ruq": "megleno-romanyayî",
"abs": "ambonezî-malezî",
"mas": "masayî",
"bdq": "bahnarî",
"mkz": "makasaeyî",
"mag": "magahî",
"oaa": "orokî",
"din": "dinkayî",
"zlw-slv": "slovînsî",
"olo": "livvî",
"mai": "maithilî",
"nhn": "nahwatliya navendî",
"nan-tws": "teoçewî",
"wuh": "wutunhuayî",
"tpw": "tûpiya kevn",
"gcf": "kreyoliya antîlleyî",
"knb": "lubuagan kalingayî",
"ket": "ketî",
"pua": "purepeçayî",
"xbr": "kamberayî",
"bor": "bororoyî",
"rmy": "romaniya vlaks",
"tao": "yamî",
"arq": "erebiya cezayirî",
"brh": "brahuiyî",
"chn": "çinook jargonî",
"cbk": "çavacanoyî",
"tvo": "tidoreyî",
"wbk": "waigalî",
"nio": "nganasanî",
"bsh": "kamkata-viriyî",
"roa-gal": "galoyî",
"krj": "kinaray-ayî",
"gmy": "yûnaniya mîkenî",
"ctd": "tedim çinî",
"ude": "udiheyî",
"gmw-rfr": "frankoniya rhine",
"tvl": "tuvalûyî",
"ibg": "îbanagî",
"ask": "aşkunî",
"ccp": "çakmayî",
"ts": "tsongayî",
"sje": "pite samî",
"esh": "iştihardî",
"quc": "k'içe'",
"ho": "hirî motuyî",
"hif": "fiji hindiyî",
"orh": "oroqenî",
"ett": "etrûskî",
"zai": "îsthmus zapotekî",
"bla": "blakfotî",
"zlw-opl": "poloniya kevn",
"ses": "sonxayî",
"lis": "lisuyî",
"kr": "kanuriyî",
"bbc": "bataktobayî",
"umb": "umbunduyî",
"rys": "yaeyamayî",
"arp": "arapaho",
"bol": "boleyî",
"com": "komançî",
"ifk": "tuwali ifugaoyî",
"ve": "vendayî",
"kri": "krioyî",
"aoz": "uab meto",
"yxg": "yagarayî",
"zom": "zouyî",
"ttt": "tatî",
"ams": "amamî-oşîmayiya başûrî",
"tts": "isanî",
"igl": "îgalayî",
"xda": "darkinjungî",
"fkv": "kvenî",
"chg": "çaxatayî",
"xwo": "oyratiya nivîsî",
"cab": "garîfunayî",
"atv": "altayiya bakurî",
"tiw": "tiwî",
"fit": "meänkiyelî",
"mnk": "mandinkayî",
"yox": "yoronî",
"tfn": "dena'inayî",
"hur": "halkomelemî",
"ryn": "amamî-oşimayiya bakurî",
"rop": "kriolî",
"ckv": "kavalanî",
"ulc": "ulçî",
"hts": "hadzayî",
"anq": "jarawa",
"dgr": "dogrîbî",
"myp": "pîrahayî",
"xfa": "faliskanî",
"gin": "hinukhî",
"pqm": "malecite-passamaquoddyî",
"mni": "manipurî",
"txg": "tangutî",
"trv": "tarokoyî",
"kha": "khasiyî",
"odt": "holendiya kevn",
"tby": "tabaruyî",
"drg": "rungusî",
"yur": "yurokî",
"neg": "negidalî",
"mga": "îrlendiya navîn",
"gor": "gorontaloyî",
"tcy": "tuluyî",
"oac": "oroçî",
"nia": "nîasî",
"skb": "saekî",
"bth": "biatah bidayuhî",
"nwy": "nottoway-meherrînî",
"chc": "katawbayî",
"bgc": "haryanvî",
"tih": "timugon murutî",
"itl": "îtelmenî",
"gml": "almaniya jêrîn a navîn",
"apj": "xîkarîyayî",
"yao": "yaoyî",
"okn": "oki-no-erabu",
"juc": "jurçenî",
"bnn": "bununî",
"nmb": "nambasiya mezin",
"cuk": "kunayî",
"uby": "ûbîkî",
"bpy": "manîpûriya bîşnûpriyayî",
"alr": "alutor",
"nys": "nyungayî",
"niq": "nandî",
"nnt": "nanticokeyî",
"shy": "şawî",
"xum": "umbriyî",
"kzg": "kikaiyî",
"blk": "pa'o karen",
"khi-kun": "ǃkungî",
"amw": "neo-aramiya rojavayî",
"kyh": "karokî",
"arw": "arawak",
"rkt": "kamtayî",
"ijc": "îzonî",
"jct": "krymçakî",
"xpq": "mohegan-pequotî",
"lnd": "lundayehî",
"xnb": "kanakanabuyî",
"niu": "nîwî",
"wls": "walîsî",
"axm": "ermeniya navîn",
"kap": "bezhtayî",
"ykg": "yukaghiriya bakurî",
"kky": "guugu yimidhirrî",
"hnj": "hmongiya hêşîn",
"dtr": "lotudî",
"kla": "klamatî-modokî",
"fax": "falayî",
"sjt": "ter samî",
"nxq": "naxî",
"oge": "gurciya kevn",
"apm": "çîrîkawayî",
"tzj": "tzutucîlî",
"tbl": "tbolî",
"inc-oas": "asamiya zû",
"hai": "haidayî",
"sou": "taiya başûrî",
"osc": "oskanî",
"shh": "şoşonî",
"xmz": "morî bawah",
"sju": "ume samî",
"xpr": "partî",
"gaa": "gayî",
"nch": "nahwatliya huastecaya navendî",
"bsk": "buruşaskiyî",
"bzj": "kreyoliya belizeyî",
"nhx": "nahwatliya mekayapan",
"ktb": "kambaatayî",
"xnn": "kankanayiya bakurî",
"pot": "potawatomî",
"agr": "aguarunayî",
"bsb": "bisayayiya bruneyî",
"cja": "çamiya rojavayî",
"nov": "noviyalî",
"kbp": "kabiyeyî",
"tkn": "toku-no-şîmayî",
"lzh": "çîniya klasîk",
"caa": "ç'orti'",
"paw": "pawnî",
"wbl": "waxiyî",
"rut": "rutulî",
"lud": "ludianî",
"gon": "gondiyî",
"lsd": "lîşana denî",
"pyu": "puyumayî",
"bej": "bejayî",
"sxr": "saaroayî",
"bsq": "bassayî",
"cmg": "mongoliya klasîk",
"mus": "krîkî",
"sas": "sasakî",
"mlm": "mulamî",
"aio": "aytonî",
"nuk": "nootkayî",
"aiw": "aarî",
"enq": "engayî",
"chl": "cahuillayî",
"huz": "hunzibî",
"grt": "garoyî",
"lic": "hlaiyî",
"fia": "nobiinî",
"osp": "spaniya kevn",
"amm": "amayî",
"ote": "otomiya mezquital",
"roa-leo": "leonîzî",
"dum": "holendiya navîn",
"awa": "awadhî",
"otw": "ottawayî",
"xbc": "baxterî",
"bem": "bembayî",
"ira-pro": "proto-îranî",
"dbl": "diyirbalî",
"mcm": "kristangî",
"ctu": "çolî",
"grk-mar": "yûnaniya mariupol",
"nst": "tangsayî",
"cak": "kaqçikelî",
"pov": "kreyoliya guinea-bissau",
"mbf": "malayiya baba",
"xsy": "saisiyatî",
"bno": "asî",
"lbj": "ladakhiyî",
"adt": "adnyamatanhayî",
"ttj": "tooroyî",
"apy": "apalayî",
"pim": "powhatanî",
"roa-brg": "bûrgûndî",
"szy": "sakizayayî",
"tkl": "tokelawî",
"wlm": "weylsiya navîn",
"gdo": "godoberî",
"ayn": "erebiya sanaanî",
"ksk": "kansayî",
"aht": "ahtnayî",
"btx": "batakiya karo",
"jdt": "tatiya cihû",
"cni": "aşanînkayî",
"skr": "seraikî",
"iir-pro": "proto-hindûîranî",
"yrl": "nhêngatûyî",
"sip": "sîkkîmî",
"ie": "înterlîngue",
"bin": "edoyî",
"sac": "foksî",
"wni": "komoriya ndzwanî",
"btq": "batekî",
"bkd": "binukid",
"gmq-scy": "skanî",
"rah": "rabhayî",
"ddo": "tsezî",
"tuw-sol": "solonî",
"gni": "gooniyandiyî",
"rmn": "romaniya balkanî",
"roa-poi": "poitevin-saintongeaisî",
"mqm": "marquesana başûr",
"tsu": "tsouyî",
"yol": "yolayî",
"fud": "futunî",
"umu": "munsî",
"wam": "massaçusettî",
"bsy": "sabah bisayayî",
"prn": "prasunî",
"clm": "klallamî",
"ssf": "thaoyî",
"jbo": "lojbanî",
"nus": "nuerî",
"mhx": "lhao voyî",
"hmc": "huişui hmongiya navendî",
"lrc": "luriya bakurî",
"tiy": "tirurayî",
"kkh": "khünî",
"nfr": "nafaanrayî",
"zpq": "zapotekiya zoogoço",
"suk": "sukumayî",
"rml": "romaniya baltîkî",
"ncj": "nahwatliya bakurê puebla",
"nhg": "nahwatliya tetelcingo",
"ars": "erebiya necdî",
"bh": "biharî",
"caq": "car nicobareseyî",
"wlc": "mwalî komorî",
"apd": "erebiya sudanî",
"mop": "mayayiya mopanî",
"yaq": "yaquî",
"pon": "ponpeyî",
"kwk": "kwak'walayî",
"wlo": "wolioyî",
"nnr": "narunggayî",
"agx": "aghulî",
"dty": "doteliyî",
"ybh": "yakkhayî",
"azz": "puebla nahuatliya çiyayî",
"yuy": "yuguriya rojhilat",
"tqw": "tonkawayî",
"hss": "harsusiyî",
"mqu": "mandarî",
"maz": "mazahuayiya navendî",
"ife": "îfèî",
"kmk": "limos kalingayî",
"shs": "şuswapî",
"hid": "hidatsayî",
"brg": "baureyî",
"wrr": "wardamanî",
"kis": "kîsî",
"acy": "erebiya qibrisî",
"tin": "tindî",
"gmq-osw": "swêdiya kevn",
"yuf": "havasupaî-walapaî-yavapaî",
"xdk": "darûkî",
"wau": "waujayî",
"ljp": "lampungî",
"alu": "areareyî",
"ach": "açolî",
"bkm": "komiya kamerûnî",
"ngn": "ngwoyî",
"bsa": "abînomnî",
"der": "deoriyî",
"shp": "şipibo-coniboyî",
"ycl": "lolopoyî",
"qua": "kuapawî",
"gmq-pro": "proto-norsî",
"nnv": "nukunuyî",
"zko": "kottî",
"khv": "khvarşiyî",
"nij": "ngajuyî",
"yog": "yogadî",
"raw": "rawangî",
"mfy": "mayoyî",
"nd": "ndebeliya bakurî",
"oko": "koreyiya kevn",
"fon": "fonî",
"abx": "abaknonî",
"rad": "radeyî",
"klj": "khalajî",
"tru": "turoyo",
"nag": "naga pidginî",
"crl": "kriya rojhilat ya bakurî",
"zlw-ocs": "çekiya kevn",
"aau": "abauyî",
"mrq": "marquesana bakur",
"sia": "samiya akkalayî",
"mjg-mgl": "mongghulî",
"car": "galîbî karîbî",
"roa-tou": "torangî",
"rel": "rendilleyî",
"oma": "omaha-poncayî",
"rmq": "kaloyî",
"rmo": "romaniya sinte",
"lep": "lepçayî",
"guw": "gunî",
"knj": "akatekî",
"slr": "salarî",
"sem-srb": "erebiya başûrî ya kevn",
"pad": "paumarî",
"yap": "yapî",
"xul": "ngunawalî",
"kzj": "kadazaniya beravî",
"fan": "fangî (Bantu)",
"brx": "bodoyî (Hindistan)",
"ane": "xârâcùù",
"cro": "crowî",
"ram": "kanelayî",
"tea": "temiarî",
"cnk": "khumi çinî",
"tkr": "tsakhurî",
"ulk": "meryamî",
"tzh": "tzeltalî",
"avk": "kotavayî",
"mjg-mgr": "mangghuerî",
"xav": "xavanteyî",
"kmv": "kreyoliya fransî ya karipúna",
"aas": "aasayî",
"guz": "gusiiyî",
"erk": "efateyiya başûr",
"ngh": "nlûyî",
"cel-gau": "gaulisî",
"hz": "hereroyî",
"alq": "algonquinî",
"cay": "cayugayî",
"inz": "înesenyoyî",
"sgr": "sangisarî",
"iro-min": "mingoyî",
"dru": "rukaiyî",
"tta": "tuteloyî",
"lui": "luiseñoyî",
"mgm": "mambayî",
"xpm": "pumpokolî",
"kyj": "karaoyî",
"ksd": "tolaiyî",
"pkc": "baekceyî",
"pcm": "pidgîniya nîjeryayî",
"kjg": "khmuyî",
"cow": "cowlitzî",
"cyo": "cuyunonî",
"msn": "vuresî",
"jhi": "jehaiyî",
"map-bms": "banyumasanî",
"fla": "montana salişî",
"skd": "sierra miwokiya başûrî",
"xpu": "punîkî",
"tsd": "tsakonî",
"gue": "gurindjiyî",
"ksi": "krisayî",
"pni": "aohengî",
"par": "panamintî",
"ulb": "ulukwumî",
"miq": "miskitoyî",
"mcb": "maçiguengayî",
"kls": "kalaşayî",
"sdo": "bukar-sadung bidayuhî",
"nr": "ndebeliya başûrî",
"mch": "maquiritarî",
"msb": "masbatenyoyî",
"tsi": "tsimşî",
"gmq-gut": "gutnişî",
"tus": "tuskarorayî",
"lmw": "miwokiya golî",
"srs": "sarceeyî",
"zag": "zaghawayî",
"klb": "kiliwayî",
"kmc": "kamiya başûrî",
"beg": "belaîtî",
"inc-ash": "prakrîtiya aşokanît",
"kj": "kwanyamayî",
"ng": "ndongayî",
"irk": "îraqwî",
"grk-ita": "grîkoyî",
"aaq": "penobskotî",
"kek": "q'eqçiyî",
"ojb": "ojibweyiya bakurî",
"txu": "kayapó",
"one": "oneidayî",
"ktv": "katuyiya rojhilatî",
"nhe": "nahwatliya huastekaya rojhilatî",
"lwl": "lawayiya rojhilatî",
"lua": "luba-kasayî",
"bjb": "barngarla",
"acu": "açuarî",
"cof": "tsafikiyî",
"gwi": "gwiçinî",
"urk": "urak lawoi'",
"tar": "tarahumarayiya navendî",
"kva": "bagvalalî",
"hni": "haniyî",
"sek": "sekanî",
"cpg": "yûnaniya kapadokyayî",
"wmb": "wambayayî",
"xld": "lîdyayî",
"amn": "amanabî",
"haa": "hänî",
"cdy": "çadongî",
"akr": "arakî",
"kju": "kaşayayî",
"ada": "adangmeyî",
"alh": "alawayî",
"gdq": "mehriyî",
"tmo": "temoqî",
"kus": "kusaalî",
"dwu": "dhuwalî",
"pak": "parakanã",
"agg": "angorî",
"wao": "wappoyî",
"aji": "ajië",
"cas": "tsimaneyî",
"byq": "basayî",
"xls": "lusîtanî",
"gcr": "kreyoliya guwiyanîsî",
"apk": "apaçiya deştan",
"kut": "kutenaiyî",
"gmw-jdt": "danmarkiya jersey",
"hus": "wastekî",
"rej": "rejangî",
"dog": "dogonî",
"roa-lor": "lorrainî",
"emb": "embalohî",
"tnq": "taînoyî",
"crj": "kriya rojhilat ya başûrî",
"nuj": "nyolî (Uganda)",
"gmq-oda": "danmarkiya kevn",
"akv": "axvaxî",
"doi": "dogriyî",
"trp": "kokborokî",
"geh": "hutterisçî",
"gul": "gullahî",
"coo": "comoxî",
"ije": "biseniyî",
"itk": "îtaliya cihû",
"rmc": "romaniya karpatyayî",
"gmw-gts": "gottscheerîşî",
"ttg": "tutongî",
"ngu": "nahwatliya guerrero",
"pse": "malayiya navendî",
"onw": "nubiya kevn",
"qui": "kuwileytî",
"giw": "gelaoyiya spî",
"ojw": "ojibweyiya rojavayî",
"gay": "gayoyî",
"rmw": "romaniya welşî",
"nhw": "nahwatliya huastecaya rojavayî",
"xkl": "bakungî",
"loz": "loziyî",
"yij": "yindcîbarndî",
"kry": "krytsî",
"ple": "palueyî",
"tht": "tahltanî",
"mkq": "bay miwokî",
"sks": "maiayî",
"mhe": "besisî",
"mrv": "mangarevanî",
"nrm": "naromî",
"chp": "çîpewî",
"bll": "biloxî",
"knx": "kendayanî",
"khw": "khowarî",
"dua": "diwalayî",
"roa-cha": "şampanyî",
"gug": "guaraniyiya paraguayanî",
"dsn": "dusnerî",
"tmh": "tuaregî",
"pmf": "pamonayî",
"smq": "samoyî",
"tal": "talî",
"yuc": "yuçî",
"mqy": "manggarayî",
"bts": "batakiya simalungun",
"tos": "totonakiya çiyayî",
"oia": "oyratayî",
"blz": "balantak",
"bjz": "baruga",
"kai": "karekareyî",
"zav": "zapotekiya yatzaçî",
"okd": "okodiayî",
"ndd": "nde-nsele-nta",
"omc": "moçikayî",
"bto": "iriga bikolanoyî",
"okr": "kirikeyî",
"top": "totonakiya papantla",
"xer": "xerenteyî",
"xlc": "lîkî",
"mid": "mandaicî",
"suy": "suyayî",
"chz": "ozumacin çinantecî",
"pah": "tenharimî",
"til": "tillamookî",
"afn": "defakayî",
"guc": "wayuuyî",
"ojp": "japoniya kevn",
"lif": "limbuyî",
"efi": "efikî",
"mek": "mêkêoyî",
"ayl": "erebiya lîbyayî",
"bnt-phu": "phuthî",
"kne": "kankanaeyî",
"ktn": "karitiyanayî",
"abl": "abûngî",
"rtm": "rotumanî",
"wyb": "ngiyambaayî",
"crg": "mîçîfî",
"kxu": "kuiyî (Hindistan)",
"cts": "bicolanoyiya northern catanduanes",
"dje": "zarmayî",
"plw": "palawanoyî",
"xlu": "luwiyanî",
"koy": "koyukonî",
"vmw": "makhuwayî",
"vmf": "frankiya rojhilatî",
"bru": "bruyiya rojhilatî",
"kmb": "kimbunduyî",
"xsa": "sabaeanî",
"tbk": "tagbanwayiya kalamî",
"kzt": "tambunan dusunî",
"xng": "mongoliya navîn",
"acn": "açangî",
"bwx": "bu-nao bunuyî",
"cwg": "çewongî",
"vmb": "mbabaramî",
"ijs": "îjoyiya başûr-rojhilat",
"asb": "assînîboyî",
"nzd": "nzadî",
"itv": "îtawitî",
"lvk": "lavukaleveyî",
"bde": "badeyî",
"tbc": "takyayî",
"aby": "aneme wakeyî",
"urb": "urubú-kaaporî",
"coh": "çiçonyi-çidzihana-çikauma",
"dbj": "ida'anî",
"smk": "bolinaoyî",
"lbx": "lawanganî",
"kfr": "kaççiyî",
"hlt": "nga layî",
"agm": "angaatahayî",
"xpg": "firîgî",
"smr": "simeulueyî",
"vma": "martuthunirayî",
"gub": "guajajarayî",
"chf": "tabasco çontalî",
"mqs": "makyaniya rojava",
"iby": "îbaniyî",
"cng": "qiangiya bakurî",
"jac": "jakaltekî",
"trw": "torwalî",
"lti": "lêtîyî",
"scl": "şinayî",
"gwr": "gwereyî",
"dgc": "casiguran dumagat agtayî",
"btm": "batakiya mandaîlîng",
"puj": "punan tubuyî",
"oka": "okanaganî",
"gek": "gerkayî",
"mat": "matlatzincayî",
"kio": "kiowayî",
"cku": "koasatî",
"kms": "kamasauyî",
"xaa": "erebiya endulusî",
"gut": "maleku jaikayî",
"aou": "a'ou",
"elx": "îlamî",
"peh": "bonanî",
"pgl": "îrlendiya prîmîtîv",
"myx": "masabayî",
"spp": "supyireyî",
"xnt": "narragansettî",
"cad": "caddoyî",
"aap": "pará arárayî",
"rme": "angloromanî",
"ura": "urarinayî",
"mpm": "mixteciya yosondúa",
"poe": "popolocayiya san juan atzingo",
"sgd": "surigaononî",
"kvh": "komodoyî",
"xtc": "kaduglî",
"tjg": "tunjungî",
"azg": "san pedro amuzgos amuzgo",
"uun": "pazehî",
"udl": "uldemeyî",
"skp": "sekapanî",
"isi": "nkem-nkumî",
"wrz": "warrayî",
"its": "îsekiriyî",
"tgb": "tobilungî",
"bdy": "bandjalangî",
"ssb": "samayiya başûrî",
"hnn": "hanunooyî",
"enn": "engenniyî",
"srm": "saramakkanî",
"dmr": "damariya rojhilat",
"djo": "jangkangî",
"kij": "kîlîvîlayî",
"rob": "tae'",
"sad": "sandaweyî",
"har": "harariyî",
"hov": "hovonganî",
"pos": "popolucayiya sayula",
"sit-bok": "bokarî",
"kns": "kensiuyî",
"mbd": "dibabawon manoboyî",
"asu": "tocantins asurînî",
"sjw": "şawneeyî",
"btw": "butuanonî",
"jut": "jutişî",
"bgs": "tagabawayî",
"xsb": "sambalî",
"mkf": "miyayî",
"mrn": "çeke holoyî",
"kui": "kuikúroyî",
"hup": "hupayî",
"abm": "abanyomî",
"ukk": "muak sa-aakî",
"lbk": "bontociya navendî",
"dbp": "duwaiyî",
"jup": "hupdëî",
"cce": "çopiyî",
"muy": "muyangî",
"agt": "agtayiya kagayanî ya navendî",
"sxn": "sangirî",
"brc": "kreyoliya holendî ya berbiceyî",
"yux": "yukaghiriya başûrî",
"zpv": "zapotekiya çiçicapan",
"teo": "atesoyî",
"dif": "dieriyî",
"maq": "mazateciya çiquihuitlan",
"sgh": "şughnî",
"gnc": "guwançî",
"ega": "egayî",
"kbh": "camsayî",
"bni": "bangî",
"tdj": "tajioyî",
"ngi": "ngizimî",
"bue": "beothukî",
"pok": "pokangayî",
"myw": "muyuwî",
"bkn": "bukîtan",
"dyu": "dyulayî",
"asn": "xingú asuriní",
"cme": "cermayî",
"tna": "takanayî",
"mia": "miamî",
"epi": "epieyî",
"mcq": "eseyî",
"kyu": "kayahiya rojavayî",
"sai-pyk": "pykobjê",
"zaa": "zapotekiya sierra de juarez",
"dhv": "dehûyî",
"bxk": "bukusuyî",
"nuz": "nahwatliya tlamacazapa",
"trm": "tregamî",
"cps": "capiznonî",
"slm": "pangutaran samayî",
"cia": "cia-ciayî",
"abv": "erebiya bahranî",
"lod": "berawanî",
"mhj": "mogholî",
"mnv": "rennelleseyî",
"gir": "gelaoyiya sor",
"bfq": "badagayî",
"wyi": "woiwurrungî",
"eya": "eyakî",
"awk": "awabakal",
"zoc": "zoqueyiya copainala",
"smw": "sumbawayî",
"ank": "goemayî",
"mbl": "maxakalî",
"tbd": "kakî aeyî",
"nez": "nez perceyî",
"crn": "corayî",
"sai-tap": "tapayunayî",
"see": "senekayî",
"huv": "huaveyî",
"apn": "apinayé",
"mfa": "malayiya pattani",
"crx": "karriyerî",
"mam": "mamî",
"ibl": "îbaloiyî",
"cac": "çujî",
"kdt": "kuyî",
"ocu": "tlahuicayî",
"hvn": "sabuyî",
"cjp": "cabecarî",
"xbi": "kombioyî",
"xra": "krahô",
"blc": "bella koola",
"peq": "pomoyiya başûrî",
"bpz": "bilbayî",
"mzp": "movimayî",
"lhu": "lahuyî",
"kge": "komeringî",
"tue": "tuyukayî",
"taa": "tananaya jêrîn",
"vkp": "kreyoliya portugalî ya korlai",
"cmo": "mnongiya navendî",
"mov": "mojaveyî",
"kkz": "kaskayî",
"zro": "zaparoyî",
"mnb": "munayî",
"mwf": "murînpatayî",
"zkg": "goguryeoyî",
"kiw": "kiwaiyiya bakur-rojhilat",
"ebr": "ebrieyî",
"pmt": "tuamotuanî",
"roa-ang": "angevinî",
"ssg": "seimatî",
"hch": "huiçolî",
"bew": "betawî",
"ycn": "yucunayî",
"mbr": "nukak makuyî",
"apq": "a-pucikwar",
"nym": "nyamwezî",
"tbi": "gaamî",
"obi": "obispeñoyî",
"pri": "paicî",
"lu": "luba-katangayî",
"kcg": "tyapî",
"yui": "yurutî",
"kvm": "kendemî",
"djk": "aukanî",
"rir": "ribunî",
"lrg": "laragiayî",
"wnw": "wintuyî",
"woe": "woleayî",
"ljl": "li’oyî",
"kic": "kîkapûyî",
"ndh": "ndalî",
"hdy": "hadiyyayî",
"cbs": "caşinahuayî",
"kys": "baram kayanî",
"nxg": "ngadhayî",
"hix": "hixkaryanayî",
"mpe": "majangî",
"pwm": "molbogî",
"nyn": "nyankoleyî",
"ilb": "îlayî",
"qya": "qiwenyayî",
"wrk": "garawayî",
"def": "dezfuliyî",
"oym": "wayampî",
"rkb": "rikbaktsayî",
"sai-msk": "masakarayî",
"itz": "îtzayî",
"sgc": "kipsigisî",
"ppi": "paipaî",
"tji": "tujiayiya bakurî",
"yak": "yakîmayî",
"gvp": "para gaviãoyî",
"jmc": "maçameyî",
"bfs": "bayiya başûrî",
"ncb": "nicobariya navendî",
"kvb": "kubuyî",
"veo": "venturenyoyî",
"poi": "popolucayiya highland",
"bny": "bintuluyî",
"bhp": "bîmayî",
"rim": "nyaturuyî",
"sog": "sogdî",
"rth": "ratahanî",
"mse": "museyî",
"ncl": "nahwatliya mîçoakanê",
"acp": "akîpayiya rojhilatî",
"pao": "paiuteyiya bakurî",
"iyx": "yaayî",
"zhx-tai": "taişanîzî",
"coe": "koreguajeyî",
"ser": "serranoyî",
"nko": "nkonyayî",
"mgq": "malilayî",
"ppo": "folopayî",
"alc": "kawésqar",
"abg": "abagayî",
"wro": "wororayî",
"kre": "panarayî",
"tuv": "turkanayî",
"dav": "taitayî",
"ppk": "umayî",
"roa-oca": "katalaniya kevn",
"tso-lya": "çolyanî",
"myu": "mundurukú",
"dcc": "deccaniyî",
"lsi": "laşiyî",
"ugo": "gongî",
"nab": "nambikwarayî",
"lil": "lillooetî",
"gqi": "guiqiongî",
"gah": "alekanoyî",
"ncz": "natçezî",
"xom": "komoyî",
"prk": "paraukî",
"bea": "beaverî",
"sky": "sikayanayî",
"pui": "puinaveyî",
"kgg": "kusundayî",
"lml": "ragayî",
"ore": "orejónî",
"vbb": "babariya başûr-rojhilat",
"mbp": "wiwayî",
"bft": "baltî",
"lgg": "lugbarayî",
"mxd": "modangî",
"mir": "isthmus mixeyî",
"enf": "forest enetsî",
"pkp": "pukapukanî",
"kzi": "kelabît bariyoyî",
"wba": "waraoyî",
"xeg": "ǁxegwî",
"cpa": "palantla çinantecî",
"men": "mendeyî",
"btu": "batuyî",
"psy": "piscatawayî",
"xhu": "hurî",
"for": "foreyî",
"deg": "degemayî",
"sai-oto": "otomacoyî",
"nhv": "nahwatliya temascaltepec",
"gme-cgo": "gothîkiya krimî",
"kyi": "kîputî",
"olt": "lîtwanyayiya kevn",
"alp": "aluneyî",
"nbb": "ndoeyî",
"uvl": "loteyî",
"pab": "parecî",
"nbh": "ngamoyî",
"dra-okn": "kannadayiya kevn",
"aak": "ankaveyî",
"wiv": "vîtûyî",
"kuz": "kunzayî",
"atb": "zaiwa",
"aca": "açaguwayî",
"zmr": "maranungkuyî",
"xke": "kerehoyî",
"isk": "îşkaşimiyî",
"dig": "digoyî",
"agn": "agutaynenî",
"bji": "burjî",
"klw": "linduyî",
"bwi": "baniwayî",
"ygr": "yagariayî",
"mez": "menomineeyî",
"kag": "kajamanî",
"mey": "hassaniyayî",
"umo": "umotinayî",
"yii": "yidinyî",
"twy": "tawoyanî",
"ktw": "katoyî",
"ari": "arîkara",
"ysr": "sirenikî",
"adz": "adzerayî",
"spo": "spokanî",
"cgg": "rukîgayî",
"wuv": "wuvulu-auayî",
"etu": "ejaghamî",
"jmd": "yamdenayî",
"mhy": "ma'anyanî",
"tae": "taryanayî",
"kzr": "karangî",
"zhn": "nong zhuangî",
"ess": "yupikiya sîberyaya navendî",
"teg": "teke-tegeyî",
"cqd": "çuanqiandian cluster miaoyî",
"sri": "sirianoyî",
"dag": "dagbaniyî",
"gae": "warekenayî",
"lre": "laurentî",
"tbt": "kitemboyî",
"pgn": "paelignianî",
"onb": "lingaoyî",
"pth": "pataxó hã-ha",
"vmg": "minigirî",
"yah": "yazghulamî",
"kxn": "kanowitî",
"kde": "makondeyî",
"api": "apiaká",
"och": "çîniya kevn",
"pre": "prînsîpenî",
"mgr": "mambwe-lunguyî",
"stp": "tepehuaniya başûr-rojhilat",
"kak": "kayapa kallahanî",
"myg": "mantayî",
"kee": "keresiya rojhilatî",
"mfh": "matalî",
"bkr": "bakumpaî",
"alw": "alaba",
"pml": "sabirî",
"rag": "logûlî",
"mei": "midobî",
"mjy": "mahicanî",
"blm": "belî",
"nhu": "nooneyî",
"kuf": "katuyiya rojavayî",
"cux": "tepeuxila cuicatecî",
"nyy": "nyakyusayî",
"mtf": "murikî (Gîneya nû)",
"mrt": "margî",
"mpj": "martu wangkayî",
"tlh": "klîngonî",
"enh": "tundra enetsî",
"sai-crd": "coroado purî",
"umi": "ukitî",
"tcb": "tanakrosî",
"kpt": "karatayî",
"xvn": "vandalî",
"xsm": "kasemî",
"ltc": "çîniya navîn",
"yuz": "yuracareyî",
"bhw": "biyakî",
"sqt": "soqotrî",
"dny": "deniyî",
"lcp": "lawayiya rojavayî",
"tpx": "akatepekiya me'phaayî",
"ake": "akawaîo",
"jeb": "jeberoyî",
"gup": "gunwingguyî",
"xeb": "eblaiteyî",
"zkt": "xîtanî",
"lex": "luangî",
"mnq": "minriqî",
"njm": "angamî",
"sge": "segayî",
"hoi": "holîkaçukî",
"tee": "huehuetla tepehuayî",
"bil": "bileyî",
"gej": "genî",
"wim": "wik-mungkanî",
"oyd": "oydayî",
"gvj": "guajayî",
"blw": "balangao",
"lgt": "pahîyî",
"sai-cac": "cacanî",
"xok": "xoklengî",
"knv": "taboyî",
"slc": "salibayî (Kolombiya)",
"noz": "nayî",
"bnq": "bantîkî",
"ykm": "yakamulî",
"dym": "yanda dogonî",
"cri": "sãotomenseyî",
"shu": "erebiya çadî",
"tcs": "creoleyiya torres strait",
"gmw-stm": "sathmar swabî",
"rbl": "miraya bikolî",
"mbn": "macaguanî",
"mgu": "mailuyî",
"pib": "yineyî",
"yku": "kuamasî",
"gva": "kaskihayî",
"sai-yur": "yurumanguî",
"clw": "çulymî",
"bln": "bîkolanoyiya başûr katanduanî",
"mxi": "mozarabîkî",
"sob": "sobeyî",
"agd": "agarabiyî",
"bxe": "ongotayî",
"ito": "îtonamayî",
"mmq": "aisî",
"gnd": "zulgo-gemzekî",
"wig": "wik-ngathanayî",
"dto": "tommo soyî",
"suz": "sunwarî",
"rol": "romblomanonî",
"llu": "lawî",
"jaa": "jamamadiyî",
"bns": "bundelî",
"wal": "wolayttayî",
"oui": "oyguriya kevn",
"bjt": "balanta-ganjayî",
"alk": "alakî",
"nhr": "naroyî",
"gmw-vog": "almaniya volga",
"nsn": "nehanî",
"kqe": "kalaganî",
"end": "endeyî",
"jui": "ngadcurî",
"hay": "hayayî",
"svb": "ulau-suainî",
"saz": "sauraştrayî",
"mep": "miriwungî",
"ert": "eritaiyî",
"yen": "yendangî",
"mxb": "mixteciya tezoatlan",
"pmw": "plains miwokî",
"fak": "fangî (Beboid)",
"sai-bet": "betoî",
"arl": "arabela",
"cks": "tayoyî",
"nrn": "nornî",
"kao": "kassonkeyî",
"blb": "bilua",
"wab": "wabî",
"kwd": "kwaioyî",
"abr": "abronî",
"wbi": "wanjî",
"cso": "soçiapam çinantecî",
"kmo": "kwomayî",
"ddw": "dawera-daweloorî",
"zae": "zapotekiya yareni",
"rac": "rasawayî",
"bra": "brajî",
"naz": "nahwatliya koatepek",
"qwm": "kipçakî",
"ayz": "maybratî",
"amp": "alamblakî",
"sit-liz": "lizuyî",
"jum": "jumjumî",
"tub": "tübatulabalî",
"hih": "pamosuyî",
"kbc": "kadiweuyî",
"tun": "tunikayî",
"cod": "cocamayî",
"abt": "ambulasî",
"ame": "yaneşa'",
"xed": "hdî",
"yrb": "yarebayî",
"mbc": "macuşî",
"sai-mal": "malalî",
"khs": "kasuayî",
"soz": "temî",
"mva": "manamî",
"bcu": "bîlîwayî",
"zpo": "zapotekiya amatlan",
"tfr": "teribeyî",
"xgf": "gabrielino-fernandeñoyî",
"aah": "abu' arapeşî",
"adn": "adangî",
"btn": "ratagnonî",
"gby": "gbariyî",
"sxb": "subayî",
"onn": "onobasuluyî",
"aih": "aî-çamî",
"djm": "jamsayî",
"wun": "bunguyî",
"bhv": "bahauyî",
"ahk": "akha",
"tow": "jemezî",
"duw": "dusun wituyî",
"alj": "alanganî",
"sbk": "safwayî",
"kho": "xotanî",
"ndj": "ndambayî",
"mmh": "mehinakuyî",
"ttw": "long watî",
"sai-cat": "catacaoyî",
"kck": "kalangayî",
"yav": "yangbenî",
"heh": "heheyî",
"szw": "sawayî",
"phk": "phakeyî",
"aoa": "angolar",
"xur": "urartûyî",
"txn": "taranganiya rojava",
"piv": "pilenî",
"xta": "mixtekiya alcozauca",
"ute": "uteyî",
"tad": "tauseyî",
"yug": "yugî",
"zum": "kumzarî",
"nkr": "nukuoroyî",
"xaw": "kawaiisuyî",
"mpa": "mpotoyî",
"zng": "mangî",
"gad": "gaddangî",
"drn": "damariya rojava",
"oca": "ocainayî",
"mcg": "mapoyoyî",
"gbm": "garhwaliyî",
"sbh": "sorî-harenganî",
"kew": "kewayî",
"mki": "dhatkî",
"obt": "bretoniya kevn",
"alo": "larike-wakasihu",
"drl": "baagandjiyî",
"tog": "tongayî (Malawî)",
"tdl": "surî",
"met": "matoyî",
"cle": "lealao çinantecî",
"gmq-jmk": "jamtîşî",
"gvf": "golinî",
"tlc": "totonakiya misantla",
"bfg": "busang kayanî",
"kvc": "kovêyî",
"kay": "kamayurayî",
"hei": "heiltsukî",
"urt": "uratî",
"nll": "nihalî",
"bdp": "bendeyî",
"ybe": "yuguriya rojavayî",
"nbr": "numanayî",
"mht": "mandahuacayî",
"wap": "wapişanayî",
"dzg": "dazagayî",
"wrg": "warunguyî",
"tiv": "tivî",
"tox": "tobî",
"kvo": "dobelî",
"ubl": "buhi'non bikolî",
"aja": "ajayî",
"toi": "tongayî (Zambiya)",
"sus": "susuyî",
"nhm": "nahwatliya morelos",
"mcf": "matsesî",
"lew": "ledo kailiyî",
"ckx": "cakayî",
"bxg": "bangalayî",
"nem": "nemî",
"mgo": "meta'",
"eno": "engganoyî",
"crd": "coeur d'aleneyî",
"bdb": "basapî",
"cui": "cuibayî",
"emx": "erromintxelayî",
"bsw": "baysoyî",
"piw": "pimbweyî",
"nim": "nilambayî",
"mzs": "makanîzî",
"njo": "aoyî",
"duk": "uyajitayayî",
"old": "moçî",
"sam": "aramiya samarî",
"mlq": "malinkî",
"awd-kaw": "kawişanayî",
"mro": "mruyî",
"sjm": "mapunî",
"bot": "bongoyî",
"lay": "lama baiyî",
"aif": "agî",
"bks": "masbate sorsogon",
"ibd": "îwaidjayî",
"dob": "dobûyî",
"noa": "woun meuyî",
"too": "totonakiya xicotepec de juarez",
"duv": "duvleyî",
"bqo": "baloyî",
"mna": "mbulayî",
"pbh": "panareyî",
"cav": "cavineñayî",
"scg": "sanggauyî",
"con": "cofanî",
"ubr": "ubirî",
"ata": "pele-ata",
"ngj": "ngieyî",
"ers": "ersuyî",
"tro": "taraoyî",
"yki": "yokeyî",
"tya": "tauyayî",
"byk": "biaoyî",
"tuq": "tedagayî",
"xrn": "arinî",
"mhq": "mandanî",
"ktx": "kaxarariyî",
"beu": "blagarî",
"mtv": "asaro'oyî",
"zca": "zapotekiya coatecas altas",
"abd": "manîdeyî",
"imn": "îmondayî",
"ofo": "ofoyî",
"avv": "avá-kanoeîro",
"ree": "rejang kayanî",
"klr": "khalingî",
"dby": "dibiyasoyî",
"bzd": "bribriyî",
"iwm": "îwamî",
"bvk": "bukatî",
"mgw": "matumbî",
"aus-wul": "wulguruyî",
"mto": "totontepec mixeyî",
"inc-ogu": "gujaratiya kevn",
"kpm": "kohoyî",
"bgz": "banggaî",
"klg": "tagakaulu kalaganî",
"xab": "sambeyî",
"pnz": "panayî (Afrîkaya navendî)",
"snl": "sangilî",
"aoc": "pemon",
"cul": "culinayî",
"nnq": "ngindoyî",
"cji": "çamalalî",
"urh": "urhoboyî",
"hoc": "hoyî",
"yok-nvy": "yokutiya geliyê bakurî",
"nbp": "nnamî",
"qyp": "kuiripî",
"ski": "sikayî",
"whk": "wahau kenyahî",
"fos": "sirayayî",
"paa-nun": "nungonî",
"mtd": "mualangî",
"mrh": "mara çinî",
"xeu": "keoru-ahiayî",
"ass": "îpulo",
"ing": "deg xinagî",
"ajg": "adjayî",
"kjl": "khamî",
"wed": "wedauyî",
"mxz": "maselayiya navendî",
"miw": "akoyeyî",
"lmc": "limilnganî",
"bag": "tukî",
"bva": "bareinî",
"yal": "yalunkayî",
"asa": "pare",
"sai-col": "colanî",
"kdd": "yankunytjatjarayî",
"pne": "penaniya rojavayî",
"hna": "minayî",
"ayp": "erebiya mezopotamyayî ya bakur",
"can": "çambriyî",
"yey": "yeyî",
"szc": "semaq beriyî",
"byd": "benyadu'",
"yok-svy": "yokutiya geliyê başûrî",
"xet": "setayî",
"aro": "araona",
"ntw": "nottowayî",
"szv": "isubuyî",
"crw": "çrauyî",
"kyz": "kayabiyî",
"asl": "asîluluyî",
"poz-abi": "abay-sembwakî",
"tsx": "mubamî",
"ify": "keley-i kallahanî",
"srv": "waray sorsogonî",
"ong": "oloyî",
"mvm": "muyayî",
"lam": "lambayî",
"icl": "zimanê destan ê îzlandê",
"xww": "wemba-wembayî",
"ixl": "îxilî",
"ape": "bukiyîpî",
"rui": "rufijî",
"mhl": "mauwakeyî",
"lgr": "lengoyî",
"nmy": "namuyî",
"ncn": "naunayî",
"pez": "penaniya rojhilatî",
"mfg": "mixiforeyî",
"bei": "bekatî'",
"ydg": "yidghayî",
"cwd": "kriya woods",
"mmn": "mamanwayî",
"nai-kat": "kathlametî",
"cje": "çruyî",
"ark": "arîkapûyî",
"fut": "futuna-anîwayî",
"wad": "wandamenî",
"tew": "tewayî",
"nyo": "nyoroyî",
"bky": "bokyî",
"tjm": "timucuayî",
"ysg": "sonagayî",
"ogu": "ogbronuagumî",
"mnj": "munjî",
"gwd": "gawwadayî",
"fbl": "west albay bikolî",
"uiv": "îyiveyî",
"bkq": "bakairí",
"ibe": "akpesî",
"maa": "mazateciya san jerónimo tecóatl",
"jig": "jinguluyî",
"tvd": "tsuvadî",
"xod": "kokodayî",
"adw": "amondawayî",
"guu": "yanomamöî",
"srh": "sarikolî",
"nam": "ngan'gityemerrî",
"hto": "minica huitotoyî",
"yby": "yaweyuhayî",
"urz": "uru-eu-wau-wau",
"ewo": "ewondoyî",
"cal": "karolînî",
"arr": "arara-karo",
"yup": "yukpayî",
"iai": "îaaiyî",
"wbh": "wandayî",
"dmw": "mudburrayî",
"xel": "keloyî",
"rma": "ramayî",
"zwa": "zayî",
"emi": "mûssaû-emîrakî",
"lai": "lambyayî",
"dim": "dimeyî",
"agu": "agwakatekî",
"zin": "zinzayî",
"kiv": "kimbuyî",
"dbg": "dogul domî",
"bzg": "babuzayî",
"roa-ole": "leoniya kevn",
"bun": "şerbroyî",
"gnn": "gumatjî",
"mhs": "bûrî",
"pln": "palenqueroyî",
"aus-guw": "guwarî",
"tgt": "tagbanwayiya navendî",
"mwv": "mentawaî",
"kke": "kakabeyî",
"pla": "mianî",
"bpr": "koronadal blaanî",
"oty": "tamîliya kevn",
"ywr": "yawuruyî",
"nai-tap": "tapaçultecî",
"sai-sec": "seçurayî",
"pid": "piaroayî",
"mih": "mixteciya çayuco",
"icr": "kreyoliya îngilîzî ya îslanderê",
"hoh": "hobyótî",
"fab": "annoboneseyî",
"ssm": "semnamî",
"ncg": "nisga'ayî",
"mkg": "makî (Çîn)",
"max": "malayiya moluccana bakur",
"zak": "zanakî",
"kxv": "kuviyî",
"xkn": "kayan river kayanî",
"fuy": "fuyugî",
"zpl": "zapotekiya laçixio",
"kwf": "kwara'aeyî",
"aig": "îngilîziya kreyolî ya antîgua û barbud",
"rai": "ramoaainayî",
"otd": "dohoyî",
"bey": "akuwagelî",
"nwr": "nawaruyî",
"tmq": "tumleoyî",
"tgx": "tagişî",
"pbr": "pangwayî",
"loj": "louyî",
"emp": "emberayiya bakurî",
"nho": "takuuyî",
"bco": "kalulî",
"ifb": "batad ifugaoyî",
"jao": "yanyuwayî",
"inc-ohi": "hindiya kevn",
"cup": "cupeñoyî",
"bci": "bawuleyî",
"ngo": "ngonî",
"nun": "anongî",
"yee": "yimasî",
"kxb": "krobuyî",
"hub": "huambisayî",
"nee": "kumakî",
"bec": "iceve-macî",
"smb": "simbarî",
"kuj": "kuriayî",
"ian": "îatmulî",
"cjv": "çuaveyî",
"zne": "zandeyî",
"ssd": "siroî",
"cbn": "nyah kurî",
"lwg": "wangayî",
"bmr": "muinanî",
"kya": "kwayayî",
"xem": "kembayanî",
"acz": "açeronî",
"pav": "wari'",
"egx-dem": "demotîkî",
"bph": "botlixî",
"lev": "lammayî",
"btp": "budibudî",
"bzq": "bulî",
"tdv": "toroyî",
"klq": "rumuyî",
"ojv": "javayiya ontongê",
"bdi": "burunî",
"wya": "wyandotî",
"pbb": "paezî",
"lfn": "lingua franca nova",
"jit": "jitayî",
"amq": "amahaî",
"tcf": "mepaya malînaltepekî",
"ebu": "embuyî",
"rwm": "ambayî",
"pnh": "penrhynî",
"and": "ansus",
"pej": "pomoyiya bakurî",
"kue": "kumanî",
"fip": "fipayî",
"kcn": "nubiyî",
"lol": "mongoyî",
"bwd": "biwaydokayî",
"aia": "arosî",
"zau": "zangskarî",
"pud": "punan aputî",
"ikz": "îkizuyî",
"bgb": "bobongko",
"thk": "tharakayî",
"ymc": "mujiya başûrî",
"mny": "manyawayî",
"xwa": "kwazayî",
"wrs": "warisî",
"nai-tip": "tipaî",
"teu": "sooyî",
"ked": "kereweyî",
"szp": "suaboyî",
"rgr": "resigaroyî",
"bdm": "budumayî",
"nib": "nakameyî",
"lek": "leyponî",
"guh": "guahiboyî",
"bqr": "burusuyî",
"apl": "lipan",
"bnd": "banda",
"tsj": "tşanglayî",
"xme-old": "mediya kevn",
"ppn": "papapanayî",
"mwp": "kala lagaw yayî",
"bfa": "bariyî",
"plh": "paulohî",
"btd": "batakiya dairî",
"ure": "uruyî",
"sty": "tatariya sîberî",
"buy": "mmaniyî",
"xyy": "yorta yortayî",
"xdy": "malayîk dayakî",
"kmj": "kumarbhag pahariayî",
"vun": "vunjoyî",
"cuh": "çukayî",
"lgl": "walayî",
"pna": "punan bah-biauyî",
"now": "nyamboyî",
"gqa": "ga'andayî",
"thm": "thavungî",
"acr": "açiyî",
"cir": "tiriyî",
"mxr": "murîkî (Malezya)",
"swp": "suauyî",
"zor": "zoqueyiya rayón",
"wic": "wiçitayî",
"suw": "sumbwayî",
"bum": "buluyî (Kamerûn)",
"rnw": "rungwayî",
"nak": "nakanayî",
"trs": "trîkuyiya çicahuaxtla",
"lpo": "lipoyî",
"gwe": "gwenoyî",
"cbd": "carijonayî",
"mot": "barî",
"tlv": "taliabuyî",
"xsu": "sanumayî",
"sjl": "mijî",
"pip": "peroyî",
"acx": "erebiya ummanî",
"ybb": "yembayî",
"uln": "unserdeutsch",
"yil": "yindjilandjî",
"ykn": "kua-nsî",
"apu": "apurinã",
"sso": "sissanoyî",
"puc": "punan merapî",
"mwn": "nyamwangayî",
"bpa": "dakakayî",
"mmd": "maonanî",
"bof": "bolonî",
"ati": "attié",
"wji": "warjî",
"lra": "rara bakati'",
"mlu": "toabaîtayî",
"bdd": "bunamayî",
"bfd": "bafutî",
"keu": "akebuyî",
"wod": "wolanî",
"ksb": "şambalayî",
"mff": "nakî",
"azn": "durango nahuatliya rojavayî ya jêrîn",
"asi": "buruwayî",
"ala": "alago",
"tpy": "trumaî",
"zms": "mbesayî",
"kib": "koalibî",
"jun": "juangî",
"guf": "gupapuynguyî",
"liy": "banda-bambariyî",
"sai-pam": "pamiguayî",
"loa": "lolodayî",
"elm": "elemeyî",
"aad": "amalî",
"lcm": "tungagî",
"twh": "tai dónî",
"ppm": "papumayî",
"xmr": "meroytîkî",
"kno": "konoyî (sierra leone)",
"kzz": "kalabrî",
"emw": "emplawasî",
"pnk": "pawnakayî",
"gym": "ngäbereyî",
"mxj": "mijuyî",
"lun": "lundayî",
"kos": "kosraeyî",
"kzx": "kamarianî",
"aan": "anambeyî",
"ukq": "ukwayî",
"tau": "tananaya jorîn",
"anc": "ngas",
"rcf": "kreyola fransî ya réunion",
"knm": "kanamariyî",
"bvy": "baybayanonî",
"jnj": "yemsayî",
"onu": "unuayî",
"bdh": "tara bakayî",
"pij": "pijaoyî",
"mks": "mixteciya silacayoapan",
"juh": "honeyî",
"etx": "etenî",
"sai-chr": "çarruayî",
"aui": "anukî",
"cuc": "usila çinantecî",
"xme-ott": "tatiya kevn",
"njj": "njenî",
"mng": "mnongiya rojhilatî",
"cih": "çinaliyî",
"ard": "arabana",
"val": "vehesî",
"aos": "taykatî",
"peb": "pomoyiya rojhilatî",
"xcb": "kumbrîkî",
"clk": "iduyî",
"aem": "aremî",
"vut": "vuteyî",
"teq": "temeinî",
"siu": "sinagenî",
"xmm": "manado malayî",
"gdj": "kurtjarî",
"xau": "kauwerayî",
"atz": "arta",
"zsr": "zapoteciya rincona başûrî",
"wmt": "walmajarrî",
"ter": "terenoyî",
"sit-sit": "situyî",
"zns": "mangasî",
"kwz": "kwadiyî",
"iki": "îkoyî",
"prq": "aşeninka pereneyî",
"ztq": "zapotekiya quioquitani-quieri",
"wan": "wanî",
"txh": "thrasî",
"ahg": "qimant",
"boe": "mundablî",
"gww": "kwiniyî",
"ior": "înorî",
"ply": "bolyuyî",
"avt": "au",
"zpu": "zapotekiya yalalagî",
"bgv": "warkay-bîpîmî",
"srb": "sorayî",
"huu": "murui huitotoyî",
"zmm": "marimanindjî",
"xme-ker": "kermanî",
"gbv": "gbanuyî",
"awe": "awetí",
"xzh": "zhang-zhungî",
"wow": "wawonî",
"tqo": "toaripî",
"syb": "subaneniya navendî",
"ril": "riangî",
"nqo": "n'koyî",
"laj": "langoyî (Uganda)",
"bbh": "buganî",
"orr": "orumayî",
"gju": "gojriyî",
"ddd": "dongotonoyî",
"yik": "dongşanba laloyî",
"sai-bag": "baguayî",
"tsr": "akeî",
"txe": "totolî",
"nal": "nalikî",
"bez": "kîbenayî",
"jeh": "jehî",
"sem-saf": "safaitîkî",
"mka": "mbreyî",
"nai-jum": "jumaytepequeyî",
"ebg": "ebughuyî",
"cbv": "cacuayî",
"lnl": "bandayiya navendî ya başûr",
"inc-kho": "xolosî",
"tzx": "tabriakî",
"sln": "salînanî",
"ncr": "ncaneyî",
"kqt": "kadazaniya çemê kliasê",
"cbr": "caşibo-cacataiboyî",
"trt": "tunggareyî",
"tyj": "tai doyî",
"kpr": "korafe-yeghayî",
"hmt": "hamtaiyî",
"csi": "coast miwokî",
"bbi": "barombî",
"tvk": "ambrîmiya başûr-rojhilat",
"ynd": "yandruwandhayî",
"sai-sap": "saparayî",
"mik": "mikasukî",
"kid": "koşinî",
"bya": "batakiya palawan",
"awd-ana": "anauyayî",
"yrw": "yarawatayî",
"tnx": "tanemayî",
"lme": "peveyî",
"nbm": "ngbaka ma'boyî",
"myy": "macunayî",
"cao": "çacoboyî",
"anp": "angîka",
"sim": "seimî",
"nha": "nhandayî",
"kqn": "kaondeyî",
"ecr": "eteocretanî",
"crv": "çaurayî",
"bef": "benabenayî",
"aud": "anuta",
"yiy": "yir-yorontî",
"usp": "uspantekoyî",
"sry": "serayî",
"njy": "njyemî",
"mod": "mobilianî",
"mmt": "malalamaî",
"leu": "karayî (Gîneya nû)",
"mbe": "molaleyî",
"txi": "ikpengî",
"snc": "sinaugoroyî",
"noc": "nukî",
"mle": "manambuyî",
"kyr": "kuruayayî",
"ivb": "îbatanî",
"sai-chu": "çuruyayî",
"lsr": "aruopî",
"zaq": "zapotekiya aloapam",
"tef": "teressayî",
"siy": "sivandî",
"nyb": "nyangboyî",
"mpi": "mpadeyî",
"tcq": "kayî",
"kbt": "gabadiyî",
"dka": "dakpayî",
"bff": "bofî",
"ahs": "aşeyî",
"zar": "zapotekiya rincón",
"xct": "tibetiya klasîk",
"afz": "obokuîtayî",
"mnf": "mundanî",
"mpw": "mapidianî",
"gza": "ganzayî",
"acv": "açumawî",
"zmu": "muruwarî",
"zmt": "maringarrî",
"lbc": "lakkiayî",
"gbg": "gbanziriyî",
"des": "desanoyî",
"kud": "awhelawayî",
"agc": "agatuyî",
"tob": "tobayî",
"suc": "subanoniya rojavayî",
"oni": "onînî",
"mne": "nabayî",
"itm": "îtu mbon uzoyî",
"ges": "geser-goromî",
"aza": "azhayî",
"nfl": "aiwooyî",
"tqp": "tomoipî",
"rof": "romboyî",
"ury": "oryayî",
"axg": "mato grosso arára",
"jax": "jambî malayî",
"gkn": "gokanayî",
"tbq-gkh": "gokhyî",
"bkz": "bungku",
"aik": "akeyî",
"tku": "totonaciya nekaksaya jorîn",
"sho": "şangayî",
"crp-rsn": "russenorskî",
"nss": "nalî",
"guo": "guayaberoyî",
"dun": "dusun deyahî",
"bsm": "busamiyî",
"ble": "balanta-kentohe",
"axx": "xaragure",
"sxg": "şixingî",
"apz": "safeyoka",
"awd-pai": "paikonekayî",
"muc": "mbu'",
"kgu": "kobolî",
"dbf": "edopiyî",
"bbp": "bandayiya rojavayî ya navendî",
"loe": "saluanî",
"nux": "mehekî",
"elk": "elkeiyî",
"yaf": "yakayî",
"tiz": "tai hongjinî",
"stu": "samtaoyî",
"nkg": "nekginî",
"koz": "korakî",
"aus-bra": "barranbinyayî",
"tlr": "talîsî",
"spc": "sapeyî",
"sai-pyg": "payaguayî",
"tbq-mil": "milangî",
"klp": "kamasayî",
"crz": "cruzeñoyî",
"boj": "ancamî",
"tan": "tangaleyî",
"svs": "savosavoyî",
"ppq": "peyî",
"ngg": "ngbaka manzayî",
"mpx": "misima-paneatî",
"kcp": "kangayî",
"cbi": "çaçiyî",
"bfc": "bayiya bakurî",
"yec": "yeniçeyî",
"sai-trr": "tarairiú",
"plg": "pilagayî",
"hoe": "horomî",
"yun": "benayî",
"any": "anyî",
"yis": "yisî",
"ush": "uşojoyî",
"tav": "tatuyoyî",
"sve": "serilî",
"gob": "playeroyî",
"nkz": "nkarî",
"moc": "mocovî",
"jko": "kuboyî",
"reg": "karayî (Tanzanya)",
"hul": "hulayî",
"chh": "çinookî",
"yub": "yugambalî",
"tbg": "tayrorayiya bakur",
"mdc": "maleyî",
"kuo": "kumukioyî",
"mvy": "indus kohistanî",
"foi": "foiyî",
"coz": "çoçotecî",
"bim": "bimobayî",
"agf": "argunî",
"kmx": "wabodayî",
"mtl": "montolî",
"kjr": "kuruduyî",
"kcb": "kawaçayî",
"ibn": "îbinoyî",
"scb": "çutî",
"atx": "arutanî",
"mvt": "mpotovoroyî",
"kxa": "kayrîrûyî",
"boq": "bogayayî",
"zpx": "zapotekiya san baltazar loxiça",
"wnc": "wantoatî",
"itd": "tidongiya başûrî",
"xog": "sogayî",
"ann": "obolo",
"unr": "mundarî",
"bzh": "mapos buangî",
"tye": "kyengayî",
"kyq": "kengayî",
"kwa": "dâwî",
"bou": "bondeyî",
"aul": "aulua",
"zty": "zapotekiya yatee",
"wrp": "waropenî",
"mmi": "musarî",
"buq": "bremî",
"auj": "awjîla",
"gtu": "aghu tharrnggalayî",
"zil": "zialoyî",
"tlb": "tobeloyî",
"mty": "nabî",
"lbz": "lardilî",
"gdd": "gêdagêdî",
"mdx": "dizinî",
"bgt": "bugotûyî",
"tpc": "azoyú me'phaayî",
"jmb": "zumbunî",
"tdn": "tondanoyî",
"rng": "rongayî",
"mtm": "matorî",
"djd": "jaminjungî",
"gol": "golayî",
"doz": "dorzeyî",
"nmk": "namakurayî",
"mnt": "maykulanî",
"lbl": "libon bikolî",
"gde": "gudeyî",
"bxs": "busamî",
"xmd": "mbudumî",
"kdq": "koçî",
"cah": "cahuaranoyî",
"bza": "bandiyî",
"aes": "alseayî",
"dsq": "tadaksahakî",
"tmj": "samarokenayî",
"att": "pamplona atta",
"xhd": "hadramî",
"brp": "barapasiyî",
"omw": "tairorayiya başûr",
"nif": "nekî",
"iyo": "mesakayî",
"luq": "lucumiyî",
"kqz": "koranayî",
"coc": "cocopayî",
"amj": "amdang",
"tlk": "talokî",
"spi": "saponî",
"nnf": "ngaingî",
"mlj": "miltuyî",
"luz": "luriya başûrî",
"xas": "kamassî",
"cua": "cuayî",
"afd": "andaî",
"yxl": "yarlî",
"sit-tgm": "tangamî",
"nga": "ngbakayî",
"mrg": "misingî",
"igm": "kanggapeyî",
"gop": "yeretuarî",
"myh": "makahî",
"kpg": "kapîngamarancîyî",
"ero": "horpayî",
"eto": "etonî (Kamerûn)",
"bcq": "bençî",
"srr": "sererî",
"mqz": "malasangayî",
"hui": "huliyî",
"cap": "çipayayî",
"uar": "tayrumayî",
"mox": "molimayî",
"nai-ipa": "ipaî",
"arh": "arhuako",
"wdj": "wadjiginyî",
"tem": "temneyî",
"snn": "sionayî",
"mxk": "monumboyî",
"kru": "kurukhî",
"inc-mas": "asamiya navîn",
"zoo": "zapotekiya asunción mixtepec",
"wme": "wambuleyî",
"pru": "puragî",
"qwt": "kwalhioqua-tlatskanaî",
"gbb": "kaytetyeyî",
"bux": "boghomî",
"atm": "ata",
"abn": "abuayî",
"zab": "zapotekiya san juan guelavia",
"wny": "wanyî",
"sai-qtm": "quitemoyî",
"mmf": "mundatî",
"gdm": "laalî",
"kxi": "keningau murutî",
"dwr": "dawroyî",
"bfw": "bondoyî",
"aux": "aurá",
"ttv": "titanî",
"nrz": "lalayî (Gîneya nû)",
"gbp": "gbaya-bossangoayî",
"awn": "awngî",
"zia": "ziayî",
"way": "wayanayî",
"sut": "subtiabayî",
"rog": "roglayiya bakurî",
"ono": "onondagayî",
"mbj": "nadëbî",
"mls": "masalitî",
"bbj": "ghomalayî'",
"dyi": "djiminiyî",
"bgg": "bugunî",
"ail": "aîmeleyî",
"wir": "wirafedî",
"cla": "ronî",
"ost": "osatuyî",
"kkr": "kir-balarî",
"dgb": "bunogeyî",
"yha": "bahayî",
"mcd": "şaranahuayî",
"rwk": "rwayî",
"ifu": "mayoyao ifugaoyî",
"lig": "ligbiyî",
"jek": "jeri kuoyî",
"ghs": "guhu-samaneyî",
"bdn": "baldemuyî",
"xkf": "xengxayî",
"tsb": "tsamaî",
"sle": "şolagayî",
"nai-spt": "sahaptinî",
"nas": "nasioyî",
"mhd": "mbuguyî",
"kfa": "kodavayî",
"jur": "jurúnayî",
"goa": "guroyî",
"efa": "efaiyî",
"aqn": "altaya bakurî",
"tjs": "tujyayiya başûrî",
"tdy": "tadyawanî",
"los": "loniuyî",
"jka": "kaerayî",
"afo": "eloyî",
"cba-cat": "catio çibçayî",
"bsn": "barasanayî",
"ama": "amanayé",
"sai-san": "sanavironî",
"prc": "paraçî",
"nej": "nekoyî",
"cms": "messapicî",
"kyl": "kalapuyayî",
"coj": "coçimiyî",
"byr": "baruyayî",
"aey": "ameleyî",
"aus-ynk": "yangkaalî",
"yta": "taluyî",
"pay": "peç",
"lse": "lusengoyî",
"cub": "cubeoyî",
"gac": "andamaniya mezin a têkel",
"yry": "yarluyandî",
"tgp": "tangoayî",
"mjt": "sawriya pahariayî",
"phl": "phalurayî",
"ngt": "ngeqî",
"xnr": "kangrî",
"ciy": "çaimayî",
"bca": "bayiya navendî",
"aog": "angoram",
"yrn": "yerongî",
"skz": "sekarî",
"pia": "pima bajoyî",
"mjb": "makaleroyî",
"hot": "hoteyî",
"cbt": "çayahuitayî",
"aor": "aore",
"uve": "uveyiya rojava",
"pbv": "pnarî",
"mkr": "malasî",
"qvy": "kueyuyî",
"nai-chi": "çiquimulillayî",
"bhi": "bhilalî",
"erg": "sieyî",
"plo": "popolucayiya oluta",
"aun": "molmo one",
"kun": "kunamayî",
"laz": "aribwatsayî",
"waj": "waffayî",
"xsi": "sioyî",
"ibr": "îbuoroyî",
"zaf": "zapotekiya ayoquesco",
"lbq": "wamparî",
"keg": "teseyî",
"nyt": "nyawaygî",
"akc": "mpur",
"mnx": "manikionî",
"kmq": "kwamayî",
"khz": "keaparayî",
"ilk": "îlongotî",
"gaj": "gadsupî",
"aqp": "atakapa",
"kgr": "abunî",
"zpf": "zapotekiya san pedro quiatoni",
"sok": "sokoroyî",
"odu": "odualî",
"muh": "mündü",
"lbb": "labelî",
"knl": "keninjalî",
"bna": "boneratî",
"auy": "auyana",
"ade": "adeleyî",
"zpw": "zapotekiya zaniza",
"opm": "oksapminî",
"mug": "musguyî",
"hob": "mariyiya awistronezî",
"ywl": "laluyiya rojavayî",
"ksx": "kêdangî",
"gbq": "gbaya-bozoumî",
"xbm": "bretoniya navîn",
"kmn": "awtuwî",
"ziw": "zigulayî",
"wbm": "wayî",
"tgo": "sudestî",
"opo": "opaoyî",
"lan": "laruyî",
"xuu": "khweyî",
"gid": "gidarî",
"bvj": "baanî",
"zzj": "zuojiang zhuangî",
"wpc": "wirö",
"rnn": "roonî",
"don": "dourayî",
"bhj": "bahingî",
"lib": "likumî",
"akj": "jeru",
"awd-gnu": "guinauyî",
"dux": "duunî",
"bqp": "busayî",
"nsk": "naskapî",
"mqb": "mbukoyî",
"kwl": "kofyarî",
"nai-jtp": "jutiapayî",
"grd": "guruntumî",
"llp": "efateyiya bakur",
"agy": "altaya başûrî",
"yar": "yabaranayî",
"khc": "tukang besiya bakur",
"sbl": "sambaliya botolan",
"crc": "lonwolwolî",
"xkg": "kagoroyî",
"hdn": "haidayiya bakurî",
"sai-ctq": "catuquinaruyî",
"bbb": "barayî",
"amr": "amarakaerî",
"yky": "yakomayî",
"bsx": "yangkamî",
"tmz": "tamanakuyî",
"zsa": "sarasirayî",
"nig": "ngalakanî",
"khl": "lusiyî",
"xow": "kowakî",
"hem": "hembayî",
"pga": "erebiya jubayê",
"bhm": "batharî",
"bzb": "andioyî",
"yko": "yasayî",
"sax": "sayî",
"ypg": "pholayî",
"nnh": "ngiemboonî",
"mpd": "maçinereyî",
"vic": "kreyoliya giravên virginê",
"kdp": "kaningdon-nindemî",
"sit-cha": "çairelî",
"yes": "yeskwayî",
"uur": "urayî (Vanuatu)",
"mhu": "taraonî",
"slu": "selaruyî",
"xtd": "mixtekiya diuxi-tilantongo",
"utr": "etuloyî",
"bnv": "benerafî",
"ydk": "yoidikî",
"trr": "tauşîroyî",
"srk": "serudung murutî",
"xpo": "poçutekî",
"nkx": "nkorooyî",
"kes": "kugboyî",
"kgm": "karipúnayî",
"cax": "çiquitanoyî",
"big": "biangaî",
"vil": "vilelayî",
"dtu": "tebul ure dogonî",
"tty": "sikaritaî",
"rmg": "norweciya traveller",
"mlw": "molokoyî",
"biq": "bipî",
"ylu": "aribwaungî",
"wga": "wagayayî",
"tnz": "ten'ednî",
"qum": "sipakapenseyî",
"pue": "puelçeyî",
"nop": "numanggangî",
"mzq": "mori atasî",
"awd-kus": "kustenauyî",
"idi": "îdî",
"kzf": "da'a kailiyî",
"bib": "bîssayî",
"zad": "zapotekiya cajonos",
"waz": "wampurî",
"tll": "tetelayî",
"pur": "puruborayî",
"nih": "nyihayî",
"tnk": "kwamerayî",
"ilv": "îlueyî",
"dmn-dam": "damayî (Sierra leone)",
"cba-ata": "atanquesî",
"abu": "abureyî",
"zpt": "zapotekiya san vicente coatlan",
"aaw": "solongyî",
"opy": "ofayeyî",
"dtb": "labuk-kinabatangan kadazanî",
"ikt": "înuvialuktunî",
"auu": "auye",
"adi": "adî",
"wsk": "waskiayî",
"tix": "tiwayiya başûrî",
"atu": "reel",
"mui": "musî",
"iso": "îsokoyî",
"gya": "gbayayiya bakur-rojava",
"auw": "awyî",
"zik": "zimakanî",
"sai-way": "wayumarayî",
"sue": "suenayî",
"sai-opo": "opónî",
"srf": "nafî",
"qxs": "kiangiya başûrî",
"ggt": "gituayî",
"bcd": "babariya bakur",
"xam": "ǀxamî",
"wiu": "wiruyî",
"rro": "roroyî",
"osi": "osingî",
"mvb": "mattolî",
"mjk": "matukarî",
"lln": "leleyî (Çad)",
"kiy": "kirikiriyî",
"sit-jap": "japhugî",
"tds": "doutaî",
"blf": "buol",
"akt": "akolet",
"apb": "sa'a",
"awd-mpr": "maypureyî",
"jee": "jerungî",
"saj": "sahuyî",
"pkn": "pakanhayî",
"kkx": "kohinî",
"nai-cal": "calusayî",
"bgf": "banganduyî",
"ybm": "yabenî",
"bhq": "tukang besî başûr",
"mxe": "mele-filayî",
"lom": "loomayî",
"hka": "kaheyî",
"emn": "emanî",
"boi": "barbarenyoyî",
"aie": "amarayî",
"anh": "nendî",
"mej": "meyahî",
"kcl": "kalayî",
"hro": "haroiyî",
"enc": "enî",
"amy": "amî",
"yde": "yangum deyî",
"taj": "tamangiya rojhilatî",
"sar": "saravekayî",
"szb": "ngalumî",
"inm": "minaeanî",
"kwh": "kowyayî",
"hed": "herdeyî",
"cuy": "cuitlatecî",
"yvt": "yaviteroyî",
"sgw": "sebat bet gurageyî",
"nph": "phomî",
"mjs": "mişipî",
"mda": "madayî (Nîjerya)",
"wat": "kaninuwayî",
"blo": "anîî",
"yss": "yessan-mayoyî",
"ona": "selk'namî",
"pny": "pinyinî",
"noe": "nimadî",
"crm": "kriya mûsî",
"hhy": "hoyahoyayî",
"bzz": "evantî",
"inc-apa": "apabhramsayî",
"uuu": "uyî",
"seu": "serui-lautî",
"pbi": "podokoyî",
"mhc": "moçoyî",
"kzw": "kaririyî",
"faa": "fasuyî",
"sai-chi": "çirinoyî",
"yab": "yuhupî",
"vit": "vitî",
"myl": "momayî",
"crb": "island caribî",
"frq": "forakî",
"cog": "çongî",
"brk": "birgidî",
"waq": "wagemanî",
"nuy": "nunggubuyuyî",
"mdb": "morigî",
"idu": "îdomayî",
"fun": "fulniôî",
"dao": "daai çinî",
"wnb": "wanambreyî",
"skv": "skouyî",
"sai-pur": "purukotó",
"nyi": "nyimangî",
"kwn": "kwangaliyî",
"kec": "keigayî",
"gbu": "gagaduyî",
"dnj": "danî",
"bus": "bokobaruyî",
"ado": "abuyî",
"zpn": "zapotekiya santa ines yatzeçi",
"aml": "war-jaîntî",
"mij": "mungbamî",
"lbu": "labuyî",
"ken": "kenyangî",
"ipo": "îpikoyî",
"zam": "zapotekoyiya mahuatlana navendî",
"wac": "wasko-wişramî",
"sww": "sowayî",
"msu": "musomî",
"rkm": "markayî",
"slp": "lamaholotî",
"gso": "gbayayiya başûr-rojava",
"ahb": "axamb",
"kit": "agobî",
"bfi": "zimanê destan ê brîtanî",
"taz": "toçoyî",
"swi": "suî",
"prm": "kibirî",
"gis": "gizigayiya bakur",
"doe": "doeyî",
"abj": "aka-beayî",
"hnh": "ǁaniyî",
"roo": "rotokasî",
"nlv": "nahwatliya orizaba",
"npl": "nahwatliya pueblaya başûr-rojhilatî",
"llc": "leleyî (Gîne)",
"fkk": "kirya-konzelî",
"mde": "bura mabangî",
"bdl": "bajwayiya endonezyayî",
"bss": "akooseyî",
"scw": "şayî",
"lsm": "saamiayî",
"pqa": "pa'ayî",
"nmq": "nambyayî",
"mbq": "maysînî",
"jib": "jibuyî",
"bhs": "buwalî",
"sjo": "xibeyî",
"abw": "pal",
"nxa": "naueteyî",
"mck": "mbundayî",
"jmx": "juxtlahuaca mixteciya rojavayî",
"clu": "caluyanunî",
"ybo": "yabongî",
"tuo": "tukanoyî",
"nml": "ndemlî",
"mfx": "meloyî",
"lop": "lopayî",
"kzv": "komyandaretî",
"kct": "kaianî",
"has": "haislayî",
"mmw": "emayî",
"cyb": "cayubabayî",
"bcj": "bardî",
"awd-ama": "amarizanayî",
"ylr": "yalarnngayî",
"ago": "taînaeyî",
"prw": "parawenî",
"enw": "enwangî",
"kog": "coguiyî",
"bst": "basketoyî",
"ymo": "yangum monî",
"uda": "udayî",
"tma": "tamayî (Çad)",
"sre": "sarayî",
"nbv": "ngamamboyî",
"mcv": "minanibaî",
"koh": "koyoyî",
"zbt": "batuî",
"anb": "andoa",
"yaw": "yawalapitî",
"tnc": "tanimuca-retuarã",
"sbx": "seberuangî",
"yhl": "hlepho phowayî",
"bzv": "bebeyî",
"anf": "anîmere",
"yet": "yetfayî",
"pir": "piratapuyoyî",
"nmw": "nimoayî",
"nhb": "bengî",
"tbo": "tawalayî",
"szd": "seruyî",
"poy": "pogoloyî",
"bve": "malayiya berau",
"ctm": "çitimaçayî",
"aaf": "aranadanî",
"mkh-mvi": "viyetnamiya navîn",
"mnd": "mondeyî",
"mup": "malvî",
"kse": "kuniyî",
"btf": "birgîtî",
"aac": "ariyî",
"tex": "tennetî",
"kbb": "kaxuyanayî",
"ula": "fungwayî",
"dsh": "daasanaçî",
"zps": "zapotekiya coatlan",
"wnp": "wanapî",
"scs": "slaveyiya bakur",
"lid": "nyindrouyî",
"kvi": "kwangî",
"idb": "îndo-portegîzî",
"bdt": "bokotoyî",
"ate": "atemble",
"zpi": "zapotekiya santa maria quiegolani",
"tvy": "timor pidginî",
"sol": "solosî",
"rjs": "rajbanşî",
"ogc": "ogbahî",
"mth": "mungguî",
"dmc": "gavakî",
"zaj": "zaramoyî",
"sss": "soyî",
"nai-okw": "okwanuçuyî",
"hia": "lamangî",
"gyg": "gbayiyî",
"duy": "dicamay agtayî",
"sgt": "brokpakeyî",
"yix": "axi yî",
"tcx": "todayî",
"sui": "sukî",
"phi-nag": "nagtipunan agtayî",
"ixc": "îxcatecî",
"giz": "gizigayiya başûr",
"baa": "babatanayî",
"akm": "aka-bo",
"wiy": "wiyotî",
"tnt": "tontemboanî",
"uta": "otankî",
"nhc": "nahwatliya tabasco",
"mph": "maungî",
"led": "lenduyî",
"kiz": "kisiyî",
"dva": "duauyî",
"bvr": "burarrayî",
"yig": "wusa nasuyî",
"neb": "tourayî",
"shv": "şehrî",
"nnm": "namiayî",
"nai-wji": "jicaqueyiya rojavayî",
"gve": "duwetî",
"yln": "buyangiya langnî",
"blq": "baluan-pam",
"jiv": "şuarî",
"quv": "sakapultekoyî",
"ynk": "naukanskî",
"mgy": "mbungayî",
"kpx": "koiariyiya çiyayî",
"ktz": "juǀ'hoanî",
"ofu": "efutopî",
"wes": "cameroon pidginî",
"dba": "bangi meyî",
"aab": "alumu-tesuyî",
"bkj": "pande",
"ndg": "ndengerekoyî",
"bbo": "konabereyî",
"zka": "kaimbulawayî",
"hgm": "haiǁomî",
"alv-ama": "amasî",
"ttm": "tutçoneyiya bakurî",
"nen": "nengoneyî",
"zmf": "mfinuyî",
"ruf": "luguruyî",
"kfq": "korkuyî",
"tmd": "haruaî",
"eot": "eotileyî",
"sem-amm": "ammoniteyî",
"tdk": "tambasî",
"sba": "ngambayî",
"lue": "luvaleyî",
"khq": "koyra çiiniyî",
"doc": "kamiya bakurî",
"sai-cul": "cullî",
"ano": "andoque",
"yww": "yawarawargayî",
"uji": "tanjijilî",
"ypz": "phuzayî",
"nsq": "miwokiya sierraya bakurî",
"sgb": "mag-ançî aytayî",
"ish": "esanî",
"aoi": "anîndîlyakwa",
"uri": "urimî",
"yer": "tarokî",
"shb": "ninamî",
"kpj": "karajayî",
"hoz": "hozoyî",
"twm": "tawang monpayî",
"mqo": "modoleyî",
"meo": "malayiya kedah",
"bbv": "karnaî",
"agl": "fembeyî",
"ctg": "çittagonianî",
"tkq": "teeyî",
"wmd": "mamaindeyî",
"kjn": "kunjenî",
"kmg": "kâteyî",
"bqq": "biritaiyî",
"aai": "arîfama-mînîafîayî",
"swj": "sirayî",
"puw": "puluwatî",
"rin": "nunguyî",
"bdo": "moromî",
"khj": "kuturmiyî",
"fvr": "furî",
"dbq": "dabayî",
"blr": "blang",
"mfi": "wandalayî",
"xsl": "slaveyiya başûr",
"pww": "pwoyiya bakurî",
"knp": "kwanjayî",
"gbi": "galelayî",
"bqc": "bokoyî",
"zpr": "zapotekiya santiago xanica",
"sai-tim": "timoteyî",
"rki": "rakhineyî",
"ogb": "ogbiayî",
"mpv": "mungkipî",
"ker": "kerayî",
"iqu": "îquitoyî",
"gou": "gavarî",
"bvf": "boorî",
"tmb": "avavayî",
"was": "waşoyî",
"tkp": "tîkopyayî",
"reb": "rembongî",
"ogo": "khanayî",
"diu": "gcirikuyî",
"orw": "oro wînî",
"nlx": "nahalî",
"vme": "maselayiya rojhilat",
"ltn": "latundê",
"bvx": "baboleyî",
"aci": "aka-karî",
"rug": "rovyanayî",
"kqs": "kissiya bakurî",
"dmu": "dubuyî",
"bwr": "burayî",
"bkc": "baka",
"akf": "akpayî",
"wut": "wutungî",
"she": "şekoyî",
"pma": "paamayî",
"mgi": "jilî",
"gvl": "gulayî",
"dyy": "dyaabugayî",
"aiy": "aliyî",
"xin": "xincayî",
"skf": "sakirabiayî",
"zmp": "mbuunî",
"bex": "jûr modoyî",
"mea": "menkayî",
"kfc": "konda-dorayî",
"kbw": "kaîepî",
"cto": "embera-catioyî",
"cam": "cemuhî",
"bae": "baré",
"tce": "tutçoneyiya başûrî",
"nfu": "mfumteyî",
"lcq": "luhuyî",
"oco": "korniya kevn",
"ktg": "kalkatunguyî",
"sja": "epenayî",
"col": "columbia-wenatçiyî",
"alx": "amol",
"wca": "yanomamî",
"xxt": "tamborayî",
"auk": "heyo",
"cuo": "cumanagotoyî",
"sai-and": "andoqueroyî",
"yva": "yawayî",
"due": "umiray dumaget agtayî",
"kml": "tanudan kalingayî",
"ptr": "piamatsinayî",
"blx": "mag-îndî ayta",
"gcl": "kreyoliya îngilîzî ya grenadî",
"ese": "ese ejjayî",
"uvh": "urî",
"nsm": "semayî",
"nin": "ninzoyî",
"hra": "hrangkholî",
"nai-taw": "tawasayî",
"sbi": "setî",
"mvq": "moereyî",
"mqg": "malayiya kota bangun kutai",
"uwa": "kuku-uwanhî",
"iow": "çiwereyî",
"bid": "bidiyoyî",
"yuk": "yukî",
"teh": "tehuelçeyî",
"mkc": "siliputî",
"zle-ono": "novgorodiya kevn",
"wmh": "waima'ayî",
"tio": "teopî",
"yae": "pumeyî",
"mqn": "moroneneyî",
"zma": "mandayî (Awistralya)",
"kdc": "kutuyî",
"kyt": "kayagarî",
"mps": "dadibî",
"byn": "blinî",
"cns": "asmatiya navendî",
"utp": "abayî",
"zpc": "zapotekiya çoapan",
"den": "slaveyî",
"pwo": "pwoyiya rojavayî",
"mub": "mubî",
"mev": "manoyî",
"bij": "kwankayî",
"kyo": "kelonî",
"inb": "îngayî",
"aph": "athpare",
"zas": "zapotekiya santo domingo albarradas",
"ral": "ralteyî",
"irn": "îrantxeyî",
"dge": "degenanî",
"boa": "borayî",
"adj": "adyukruyî",
"zal": "zauzouyî",
"wax": "watamî",
"tca": "tîkunayî",
"agv": "remontado agta",
"omg": "omaguayî",
"xru": "marriammuyî",
"khn": "khandeşiyî",
"gji": "gejiyî",
"ayd": "ayabadhu",
"zmb": "zimbayî",
"toj": "tojolabalî",
"syk": "sukurî",
"bnf": "masiwang",
"glw": "glavdayî",
"bav": "babungoyî",
"akx": "aka-kede",
"ots": "otomiya estado de meksîka",
"mjj": "mawakî",
"leh": "lenjeyî",
"kss": "kissiyiya başûrî",
"kbz": "duhwayî",
"sth": "şeltayî",
"myf": "bambassî",
"ald": "alladînî",
"ego": "eggonî",
"cbu": "candoşi-şaprayî",
"tmc": "tumakî",
"pgs": "pangsengî",
"ndz": "ndogoyî",
"knd": "kondayî",
"haj": "hajongî",
"cmi": "embera-çamiyî",
"mlp": "bargamî",
"yat": "yambetayî",
"bsg": "başkardiyî",
"pek": "pençalî",
"lwo": "luwoyî",
"kpk": "kpanî",
"kdx": "kamî",
"cug": "cungî",
"anx": "andra-hus",
"sey": "secoyayî",
"pio": "piapocoyî",
"jgo": "ngombayî",
"miy": "mixteciya ayutla",
"gyn": "kreyoliya îngilîzî ya guyanî",
"hoa": "hoavayî",
"amx": "anmatyerre",
"tdm": "tarumayî",
"sza": "semelayî",
"pit": "pitta-pittayî",
"nsz": "nisenanî",
"mis-mmd": "mîmiya decorseyî",
"mky": "makianiya rojhilat",
"kdj": "karamojongî",
"hre": "hreyî",
"bie": "bepourî",
"ena": "apaliyî",
"vgr": "vaghrî",
"sze": "sezeyî",
"not": "nomatsiguengayî",
"mif": "mofu-gudurî",
"mfp": "malayiya makassar",
"kul": "kulereyî",
"huo": "huyî",
"biu": "biete",
"twe": "teiwayî",
"stv": "silt'eyî",
"mqj": "mamasayî",
"kug": "kupayî",
"chw": "çuaboyî",
"zkr": "zakhringî",
"waw": "waiwaî",
"ntp": "tepehuaniya bakurî",
"pmi": "pumiya bakurî",
"hux": "nüpode huitotoyî",
"mor": "moroyî",
"mha": "mandayî (Hindistan)",
"thd": "kuuk thaayorreyî",
"dgz": "dagayî",
"mae": "bo-rukulî",
"sjg": "assangorî",
"zpg": "zapotekiya guevea de humboldt",
"tcz": "thado çinî",
"nyv": "nyulnyulî",
"aso": "dano",
"bld": "bolango",
"snk": "soninkeyî",
"sai-mar": "maratinoyî",
"laf": "lafofayî",
"krk": "kerekî",
"gyl": "gayilî",
"ksr": "borongî",
"avi": "avîkam",
"wah": "watubelayî",
"tpl": "tlacoapa me'phaayî",
"mfr": "marrithiyelî",
"khr": "khariayî",
"giq": "gelaoyiya hêşîn",
"aye": "ayere",
"owl": "weylsiya kevn",
"zsu": "sukurumî",
"nhk": "nahwatliya cosoleacaque",
"law": "laujeyî",
"izr": "îzereyî",
"goi": "gobasiyî",
"wog": "wogamusinî",
"ttu": "torauyî",
"dts": "toro so dogonî",
"mcw": "mawayî",
"azc-ktn": "kitanemukî",
"ddj": "jaruyî",
"dma": "dumayî",
"bkh": "bakoko",
"aqz": "akuntsu",
"pdo": "padoeyî",
"nnb": "nandeyî",
"dzl": "dzalayî",
"bax": "bamumî",
"xiy": "xipayayî",
"tct": "t'enî",
"lht": "lo-togayî",
"kkk": "kokotayî",
"lot": "lotukoyî",
"tdc": "embera-tadó",
"bjc": "barijî",
"aew": "ambakîç",
"tkm": "takelmayî",
"sbp": "sanguyî (Tanzanya)",
"sai-ptg": "patagónî",
"lgk": "neververî",
"ybj": "haşayî",
"amg": "amurdag",
"yns": "yansî",
"udu": "udukî",
"mwm": "sarî",
"ums": "pendauyî",
"wii": "minidienî",
"pbn": "kpasamî",
"cuq": "cunî",
"bbd": "bauyî",
"aty": "aneityum",
"ngc": "ngombeyî (Kongo)",
"kxo": "kanoeyî",
"ags": "esîmbî",
"bxb": "belanda borî",
"urn": "uruangnirinî",
"mis-mmn": "mîmiya naçtigalî",
"hru": "hrusoyî",
"apt": "apatanî",
"yon": "yongkomî",
"vag": "vaglayî",
"vkl": "kulisusuyî",
"kxh": "karoyî",
"jab": "hyamî",
"smg": "simbalî",
"asj": "nsarî",
"knk": "kurankoyî",
"kaf": "katsoyî",
"yml": "îamaleleyî",
"brt": "bitareyî",
"bao": "waîmaha",
"tla": "tepehuaniya başûr-rojavayî",
"pmj": "pumiya başûrî",
"nuf": "nusuyî",
"mrl": "mortlockeseyî",
"man": "mandingoyî",
"ign": "îgnacianoyî",
"bzn": "boanoyî (Maluku)",
"xss": "assanî",
"wne": "wanecî",
"thf": "thangmî",
"kjb": "q'anjob'alî",
"sai-muc": "mucuçî",
"msw": "mansoankayî",
"kem": "kemakî",
"bnp": "bolayî",
"aqm": "atohwaim",
"zat": "zapotekiya tabaa",
"sov": "sonsorolî",
"lhn": "lahananî",
"kjq": "keresiya rojavayî",
"dei": "demisayî",
"kwi": "awa-cuaiquerî",
"aez": "aekayî",
"zay": "zayse-zergullayî",
"una": "watutiya bakur",
"ctl": "tlacoatzintepec çinantecî",
"squ": "squamîşî",
"omb": "ombayî",
"sur": "mwaghavulî",
"umc": "marrusînî",
"lbn": "lametî",
"phi-din": "dinapigue agtayî",
"buw": "bubiyî",
"ahn": "ahàn",
"zrn": "zirenkelî",
"srg": "sulodî",
"ora": "orohayî",
"mtn": "matagalpayî",
"kco": "kinalaknayî",
"hmd": "a-hmaoyî",
"woc": "wogeoyî",
"tqr": "toronayî",
"rbb": "rumaî palaungî",
"ott": "otomiya temoaya",
"nhi": "nahwatliya zacatlan-ahuacatlan-tepetzintla",
"mgk": "mawesî",
"dus": "dumiyî",
"bzu": "burmesoyî",
"bbu": "bakulungî",
"trn": "trinitarioyî",
"sdp": "şerdukpenî",
"sbv": "sabineyî",
"sai-jrj": "jirajirayî",
"gvs": "gumawanayî",
"dnn": "dzuunî",
"tte": "bwanabwanayî",
"bcf": "bamuyî",
"xir": "xiriânayî",
"two": "tswapongî",
"sai-tab": "tabancaleyî",
"lgq": "logbayî",
"klm": "kolomî",
"ngs": "gvokoyî",
"eka": "ekajukî",
"dbw": "bankan tey dogonî",
"aly": "alyawarr",
"yad": "yaguayî",
"ndp": "ndoyî",
"mcr": "menyayî",
"xon": "konkombayî",
"kka": "kakandayî",
"hao": "haköyî",
"ckl": "cibakî",
"jbk": "barikewayî",
"abc": "ambala ayta",
"yaa": "yaminahuayî",
"sny": "saniyo-hiyeweyî",
"mmc": "miçoacan mazahuayî",
"xkq": "koronî",
"had": "hatamî",
"sai-cop": "copallenî",
"swy": "saruayî",
"nid": "ngandî",
"mhi": "ma'dî",
"cpo": "kpeeyî",
"anz": "anem",
"unz": "unde kailî",
"sai-tpr": "taparitayî",
"sed": "sedangî",
"fpe": "piçinglisî",
"mxy": "mixteciya noçixtlana başûr-rojhilatî",
"bfz": "mahasu paharî",
"sai-esm": "esmeraldayî",
"urv": "uruavayî",
"seh": "senayî",
"spx": "pîkeneyiya başûr",
"cag": "nivacleyî",
"zkk": "karankawayî",
"bkl": "berik",
"yut": "yopnoyî",
"vai": "vaî",
"slw": "sialumî",
"noj": "nonuyayî",
"caf": "karrieriya başûrî",
"fag": "finonganî",
"ibh": "bihî",
"awt": "araweté",
"tyx": "teke-tyeeyî",
"mog": "mongondowî",
"knt": "katukinayî",
"bwm": "biwatî",
"apr": "arop-lokep",
"atr": "waîmîrî-atroarî",
"mtt": "motayî",
"pym": "fyamî",
"dlk": "dahalikî",
"bzl": "boanoyî (sulawesî)",
"abp": "abenlen ayta",
"zaw": "zapotekiya mitla",
"svm": "slavomolisanoyî",
"zpz": "zapotekiya texmelucan",
"tit": "tiniguayî",
"rax": "rangî",
"asx": "muratayak",
"mvr": "marauyî",
"lha": "lahayî (Viyetnam)",
"irh": "îrarûtûyî",
"sai-gay": "gayónî",
"awi": "aekyom",
"mcy": "watutiya başûr",
"tpt": "tlaçiçilco tepehuayî",
"sto": "stoneyî",
"rea": "rerauyî",
"aom": "ömie",
"tlx": "khehekî",
"mrx": "dineorî",
"oro": "orokoloyî",
"acl": "akar-baleyî",
"wbw": "woî",
"otn": "otomiya tenango",
"gia": "kitjayî",
"ats": "gros ventre",
"tri": "trió",
"zmz": "mbandjayî",
"yzg": "e'ma buyangî",
"bwa": "bwatûyî",
"bcw": "banayî",
"ywt": "xişanba laloyî",
"tsc": "tswayî",
"gmw-tsx": "saksoniya transylvanyayê",
"fap": "palorî",
"nwa": "nawathinehenayî",
"mue": "media lenguayî",
"kcx": "kaçama-ganjuleyî",
"dah": "gwahatikeyî",
"ekg": "ekariyî",
"cbc": "carapanayî",
"bxw": "bankayî",
"ygw": "yagwoiayî",
"tbr": "tumtumî",
"tgw": "tagwana senoufoyî",
"pnr": "panimî",
"njt": "ndyuka-trio pidginî",
"vnk": "lovonoyî",
"kma": "konniyî",
"hal": "halangî",
"awc": "cicipu",
"bch": "bariyayî",
"gfk": "patpatarî",
"ule": "luleyî",
"vkm": "kamakanî",
"xht": "hattîkî",
"btz": "batakiya alas-kluet",
"dti": "ana tinga dogonî",
"yre": "yaoureyî",
"udj": "ujirî",
"tpu": "tampuanî",
"ung": "ngarinyinî",
"xjb": "minjungbalî",
"slz": "ma'yayî",
"kpe": "kpelleyî",
"xla": "kamulayî",
"kpc": "curripacoyî",
"bvz": "bawziyî",
"ets": "yekheeyî",
"bbn": "uneapayî",
"tvw": "sedoayî",
"crp-spp": "pidginiya samoan plantation",
"ngq": "ngoremeyî",
"mil": "mixteciya peñoles",
"sai-chn": "çanayî",
"kvu": "yinbaw karenî",
"txx": "tatanayî",
"lva": "maku'ayî",
"gel": "fakkanciyî",
"bom": "beromî",
"cvn": "valle nacional çinantecî",
"sya": "siangî",
"bql": "bilakurayî",
"arv": "arbore",
"sai-yup": "yupuayî",
"vnp": "vunapuyî",
"sbb": "simboyî",
"gmw-pro": "proto-cermeniya rojava",
"nbn": "kurî",
"wbv": "wajarrî",
"mwh": "moûk-arîakî",
"bwp": "mandobo bawahî",
"fie": "fyerî",
"dal": "dahaloyî",
"aba": "abeyî",
"zpm": "zapotekiya mixtepec",
"wky": "wangkayutyuruyî",
"laq": "qabiaoyî",
"nja": "nzanyî",
"dmo": "kemezungî",
"dro": "daro-matu melanauyî",
"ato": "atong (Kamerun)",
"yif": "açeyî",
"zts": "zapotekiya tilquiapan",
"lah": "lahndayî",
"iry": "îrayayî",
"roa-bbn": "bourbonnais-berriçonî",
"aal": "afadeyî",
"czn": "zenzontepekiya çatîno",
"mpl": "watutiya navîn",
"lmg": "lamogayî",
"nlg": "gelayî",
"ayo": "ayoreoyî",
"zoh": "zoqueyiya çimalapa",
"mtp": "wiçi lhamtes noctenî",
"suv": "sulungî",
"xmt": "matbatî",
"zga": "kingayî",
"jbt": "jabutiyî",
"gof": "gofayî",
"rmt": "domarî",
"bwu": "bulî (Gana)",
"jod": "wojenakayî",
"yyu": "torricelli yauyî",
"sbf": "şaboyî",
"yan": "mayangnayî",
"kii": "kitsaiyî",
"trc": "trîkuyiya copala",
"shx": "şeyî",
"pgu": "paguyî",
"gwa": "mbatoyî",
"dyo": "jola-fonyiyî",
"yas": "gunuyî",
"igb": "ebirayî",
"plz": "paluanî",
"alv-gwa": "gwarayî",
"eke": "ekitî",
"cfd": "carayî",
"abb": "bankonî",
"amc": "amahuaca",
"rhp": "yahangî",
"lmb": "mereiyî",
"koa": "konomalayî",
"hlb": "halbiyî",
"eme": "emerillonî",
"nai-cig": "ciguayoyî",
"ael": "ambeleyî",
"jay": "yan-nhanguyî",
"tak": "talayî",
"mmy": "migaamayî",
"lmd": "lumunî",
"kfp": "korwayî",
"woi": "kamangî",
"akg": "anakalunguyî",
"nai-per": "pericú",
"nrl": "ngarlumayî",
"mew": "maakayî",
"hig": "kamweyî",
"hnd": "hindkoyiya başûrî",
"cuv": "cuvokî",
"ylg": "yeloguyî",
"une": "unemeyî",
"tpj": "tapieteyî",
"sos": "seenkuyî",
"nsh": "ngoşieyî",
"mit": "mixteciya pueblaya başûr",
"mxx": "mahouyî",
"bzk": "kreyoliya nîkaraguayî",
"esq": "esselenî",
"nbc": "çangî",
"anu": "anuak",
"snu": "senggî",
"mpu": "makurapî",
"hbb": "hubayî",
"cbg": "çimilayî",
"wti": "bertayî",
"app": "apma",
"spg": "sianî",
"snf": "noonî",
"mkp": "moikodî",
"viv": "idunayî",
"gem-pro": "proto-cermenî",
"omx": "moniya kevn",
"maw": "mamprulî",
"are": "arrernteya rojavayî",
"zah": "zangwalî",
"ssl": "sisaalayiya rojavayî",
"mpc": "mangarayî",
"qun": "kuynaultî",
"gyd": "kayardildî",
"ikx": "îkî",
"dij": "daiyî",
"bwq": "boboyiya başûrî",
"aum": "asu",
"axb": "abipon",
"zac": "zapotekiya ocotlan",
"xwk": "wangkumarayî",
"nzi": "nzimayî",
"mzv": "manzayî",
"kwe": "kwerbayî",
"sjk": "kemi samî",
"gmv": "gamoyî",
"bkt": "bolokî",
"atw": "atsugewî",
"zpk": "zapotekiya tlacolulita",
"wbe": "waritaî",
"ray": "rapayî",
"ojs": "ojibweyiya severnê",
"zmd": "maridanî",
"kxz": "kerewoyî",
"irr": "îrî",
"gbr": "gbagyiyî",
"bzx": "bozoyiya hainyaxo",
"aya": "awar",
"res": "reşeyî",
"oin": "oneyiya inebu",
"mwe": "mwerayî",
"ite": "îteneyî",
"gaf": "gendeyî",
"gdl": "diraşayî",
"nfd": "ahwayî",
"tlm": "tolomakoyî",
"mgv": "matengoyî",
"gqn": "kinikinaoyî",
"jah": "jah hutî",
"gog": "gogoyî",
"doy": "dompoyî",
"bfj": "bafanjî",
"xwc": "wokkonî",
"tei": "torricellî",
"sha": "şall-zwallî",
"stn": "owayî",
"emy": "mayaniya epigrafîk",
"azd": "durango nahuatliya rojavayî",
"bls": "balaesang",
"dul": "alabat island agtayî",
"xan": "xamtangayî",
"plk": "şinayiya kohistanî",
"sai-sac": "sacatayî",
"apf": "pahanan agta",
"npa": "nar phuyî",
"mfn": "mbembeyiya çema crossê",
"czk": "knaanicî",
"jow": "jowuluyî",
"cbb": "cabiyariyî",
"bpq": "banda malayî",
"sit-tsh": "tşobdunî",
"sbe": "salibayî (Gîneya nû)",
"mfj": "mefeleyî",
"lok": "lokoyî",
"kdu": "kadaruyî",
"ekp": "ekpeyeyî",
"bap": "bantawa",
"ali": "amaimonî",
"ynu": "yahunayî",
"tuf": "tuneboyiya navendî",
"ppu": "paporayî",
"dcr": "negerhollandsî",
"meq": "mereyî",
"nai-knm": "konomihuyî",
"ijn": "kalabariyî",
"hla": "haliayî",
"alm": "amblongî",
"ynn": "yanayî",
"ntj": "ngaanyatjarrayî",
"kbq": "kamanoyî",
"anm": "analî",
"ukg": "ukurigumayî",
"mav": "satere-maweyî",
"mfz": "mabaanî",
"kqq": "krenakî",
"hns": "hindustaniyiya karibî",
"taf": "tapirapeyî",
"mig": "mixteciya san miguel el grande",
"kni": "kanufiyî",
"sai-chp": "çapacurayî",
"cko": "anufoyî",
"wnu": "usanî",
"spe": "sepayî (Gîneya nû)",
"xrw": "karawayî",
"bnt-cmw": "çimwiinî",
"vam": "vanimoyî",
"sjr": "siar-lakî",
"poo": "pomoyiya navendî",
"mlz": "malaynonî",
"xks": "kumbewahayî",
"ldb": "idunî",
"tqb": "tembeyî",
"nai-sin": "sinacantanî",
"nur": "nuguriayî",
"mqx": "mamujuyî",
"unn": "kurnayî",
"bcn": "bîbaalî",
"ttr": "terayî",
"wkd": "moyî",
"kzl": "kayeliyî",
"ntk": "îkomayî",
"gdg": "ga'dangî",
"gbj": "bodo gadabayî",
"abi": "abîdjî",
"zao": "zapotekiya ozolotepec",
"sai-wnm": "wanhamî",
"thp": "thompsonî",
"cwe": "kwereyî",
"xbn": "kenaboî",
"gnq": "ganayî",
"tcc": "datoogayî",
"ztg": "zapotekiya xanaguia",
"zmj": "maridjabinî",
"lak": "lakayî",
"kvr": "kerînçî",
"ggu": "gbanî",
"anv": "denya",
"aof": "bragat",
"awr": "awera",
"zhb": "zhabayî",
"www": "wawayî",
"stb": "subaneniya bakurî",
"ret": "rettayî",
"oon": "öngeyî",
"mlv": "mwotlapî",
"mzr": "marúboyî",
"lag": "langiyî",
"kxc": "khonsoyî",
"itx": "îtikî",
"bja": "budza",
"ayu": "ayuyî",
"tba": "aikanayî",
"txm": "tominî",
"orx": "oroyî",
"nhq": "nahwatliya huaxcaleca",
"mpq": "matisî",
"kfk": "kinnauriyî",
"kzh": "dongolawiyî",
"bck": "bunabayî",
"ksf": "bafiayî"
},
"numberLabels": {
"y": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"s": {
"display": "\u003Cabbr title=\"yekjimar\"\u003Ey\u003C/abbr\u003E"
},
"d": {
"cat": "dualia tantum",
"display": "\u003Cabbr title=\"hejmar dual\"\u003Edu\u003C/abbr\u003E"
},
"p": {
"cat": "POS pirjimar",
"display": "\u003Cabbr title=\"pirjimar\"\u003Ep\u003C/abbr\u003E"
},
"p+": {
"cat": "POS axlebî pirjimar",
"display": "axlebî pirjimar"
},
"tp": {
"cat": "POS tenê pirjimar",
"display": "tenê pirjimar"
},
"j": {
"cat": "POS jimarbar",
"display": "[[jimarbar]]"
},
"j+": {
"cat": "POS axlebî jimarbar",
"display": "axlebî jimarbar"
},
"j/nj": {
"cat": "POS jimarbar û nejimarbar",
"display": "[[jimarbar]] û [[nejimarbar]]"
},
"nj": {
"cat": "POS nejimarbar",
"display": "[[nejimarbar]]"
},
"nj+": {
"cat": "POS axlebî nejimarbar",
"display": "axlebî [[nejimarbar]]"
},
"hj!": {
"display": "hejmar nepeyitandî ye"
},
"hj?": {
"req": true,
"display": "hejmar hewce ye"
}
}
}
ca7hwonq564f08qpsjzoou0vyda86ry
MediaWiki:Gadget-test
8
1358589
6334098
6333723
2026-08-16T13:55:36Z
Wikihez
7344
6334098
wikitext
text/x-wiki
[[MediaWiki:Gadget-test.js|test.js]] — Ji bo ceribadinê hatiye çêkirin, ji kerema xwe eger nizanin çi ye, aktîv nekin!
brqqj4gehsuz2r5flelq9tahqqld1zw
Bikarhêner:Wikihez/Gadget-test/util.js
2
1358591
6334274
6333825
2026-08-16T23:35:51Z
Wikihez
7344
6334274
javascript
text/javascript
/**
* Generic text and UI helpers for the entry-creation wizard; no wizard-specific
* state, so any gadget can require("./start/util.js").
*/
//<nowiki>
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function splitTercimeIzah(mane) {
var m = /^([\s\S]*?)\s*\{\{îzah\|([\s\S]*)\}\}\s*$/.exec(mane || "");
return m ? { tercime: m[1], izah: m[2] } : null;
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
var FOLD_EXTRA ={
"ı":"i", "ø":"o","đ":"d","ð":"d","ə":"e","ħ":"h","ł":"l","ŋ":"n","ʉ":"u", "ɨ":"i","ʒ":"z","ɛ":"e","ɔ":"o","æ":"ae", "œ":"oe","ß":"ss","þ":"th"
};
function foldDiacritics(str) {
if (str === null || str === undefined) return "";
var s = String(str).toLowerCase();
if (typeof s.normalize === "function") {
// Split "î" into "i" + combining circumflex, then drop the accent.
s = s.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
return s.replace(/[^\u0000-\u007f]/g, function (ch) {
return Object.prototype.hasOwnProperty.call(FOLD_EXTRA, ch) ? FOLD_EXTRA[ch] : ch;
});
}
var WIKTKU_RESIZE_STORAGE_PREFIX = "wiktku_resize_height_";
function wiktkuResizeStorageKey(textarea) {
var cls = (textarea.attr("class") || "").trim().replace(/\s+/g, "_");
return WIKTKU_RESIZE_STORAGE_PREFIX + cls;
}
function wiktkuSaveResizeHeight(textarea, height) {
if (!(window.mw && mw.storage)) return;
try {
mw.storage.set(wiktkuResizeStorageKey(textarea), String(Math.round(height)));
} catch (e) { /* localStorage unavailable (private mode, quota, …) — skip caching */ }
}
function wiktkuLoadResizeHeight(textarea) {
if (!(window.mw && mw.storage)) return null;
try {
var v = mw.storage.get(wiktkuResizeStorageKey(textarea));
return v ? parseFloat(v) : null;
} catch (e) {
return null;
}
}
function wiktkuMakeManualResizable(textarea) {
textarea = $(textarea);
if (!textarea.is("textarea") || textarea.hasClass("wiktku-start-narrow")) return;
if (textarea.data("wiktkuResizableDone")) return;
textarea.data("wiktkuResizableDone", true);
textarea.wrap("<div class='wiktku-resize-wrap'></div>");
var grip = $("<div class='wiktku-resize-grip'></div>");
textarea.after(grip);
// Restore this field's last saved height, if any.
var cachedHeight = wiktkuLoadResizeHeight(textarea);
if (cachedHeight) {
var minHeightInit = parseFloat(textarea.css("min-height")) || 0;
textarea.css("height", Math.max(cachedHeight, minHeightInit) + "px");
}
var dragging = false, startY = 0, startHeight = 0;
function onPointerMove(e) {
if (!dragging) return;
var oe = e.originalEvent || e;
var minHeight = parseFloat(textarea.css("min-height")) || 0;
var newHeight = startHeight + (oe.clientY - startY);
textarea.css("height", Math.max(newHeight, minHeight) + "px");
}
function onPointerUp(e) {
if (!dragging) return;
dragging = false;
var oe = e.originalEvent || e;
try { grip[0].releasePointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).off("pointermove", onPointerMove);
$(document).off("pointerup pointercancel", onPointerUp);
wiktkuSaveResizeHeight(textarea, textarea.outerHeight());
}
grip.on("pointerdown", function (e) {
var oe = e.originalEvent || e;
if (oe.button !== undefined && oe.button !== 0) return; // primary contact only
e.preventDefault();
dragging = true;
startY = oe.clientY;
startHeight = textarea.outerHeight();
try { grip[0].setPointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).on("pointermove", onPointerMove);
$(document).on("pointerup pointercancel", onPointerUp);
});
}
/** Applies wiktkuMakeManualResizable to every eligible textarea in `context` (itself or its descendants). */
function wiktkuInitManualResize(context) {
var $context = $(context);
var $textareas = $context.is("textarea") ? $context : $context.find("textarea");
$textareas.each(function () {
wiktkuMakeManualResizable(this);
});
}
/** URL of a template's own wiki page, via mw.util when available. */
function tplPageUrl(name) {
var title = "Şablon:" + name;
if (window.mw && mw.util && typeof mw.util.getUrl === "function") {
return mw.util.getUrl(title);
}
return "/wiki/" + encodeURIComponent(title);
}
function wrapNativeSelect(select) {
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
if (select.prop("multiple")) {
return wrapper.append(select);
}
var placeholder = select.attr("data-placeholder") || "Lê bigere...";
select.hide();
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.attr("role", "combobox")
.attr("aria-autocomplete", "list")
.attr("aria-expanded", "false")
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results' role='listbox'></ul>").hide();
var allOptions = [];
var shown = [];
var activeIndex = -1;
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
var text = $(this).text();
// Skip a blank value with no label; a blank value WITH a label
// (e.g. "clear the selection") is still a real, selectable option.
if (!v && !text) return;
// Language options read "Name (code)" -- score the name alone so
// the trailing code doesn't also count as a name match.
var name = text.replace(/\s*\([^()]*\)\s*$/, "");
allOptions.push({
value: v,
text: text,
disabled: $(this).prop("disabled"),
// data-tpl options render as a template row (name, page link, info icon).
tpl: $(this).attr("data-tpl") || "",
tplStatus: $(this).attr("data-tpl-status") || "",
foldCode: foldDiacritics(v),
foldName: foldDiacritics(name),
foldText: foldDiacritics(text)
});
});
}
cacheOptions();
// True if `q` starts a word inside `name` ("kevn" in "ingiliziya kevn").
function startsWord(name, q) {
var from = 0, at;
while ((at = name.indexOf(q, from)) !== -1) {
if (at === 0 || /[\s\-\u2013\u2014(/,.]/.test(name.charAt(at - 1))) return true;
from = at + 1;
}
return false;
}
// Lower rank = better match; a code match always outranks a name match.
var RANK_NONE = 99;
function rankOption(o, q) {
var code = o.foldCode, name = o.foldName;
if (code === q) return 0; // exact code
if (code.indexOf(q) === 0) return 1; // code prefix
if (name === q) return 2; // exact name
if (name.indexOf(q) === 0) return 3; // name prefix
if (startsWord(name, q)) return 4; // starts a word in the name
if (name.indexOf(q) !== -1) return 5; // name substring
if (code.indexOf(q) !== -1) return 6; // code substring
if (o.foldText.indexOf(q) !== -1) return 7; // anywhere else in the label
return RANK_NONE;
}
function positionList() {
// Position the list above or below depending on viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px = 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
}
function closeList() {
resultsList.hide();
textInput.attr("aria-expanded", "false");
activeIndex = -1;
}
function showResults(query) {
var q = foldDiacritics($.trim(query || ""));
resultsList.empty();
activeIndex = -1;
var matched = [];
if (q) {
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var rank = rankOption(o, q);
if (rank !== RANK_NONE) matched.push({ opt: o, rank: rank, i: i });
});
if (!matched.length && q.indexOf(" ") !== -1) {
var tokens = q.split(/\s+/);
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var all = true;
$.each(tokens, function (_, t) {
if (o.foldText.indexOf(t) === -1) { all = false; return false; }
});
if (all) matched.push({ opt: o, rank: 8, i: i });
});
}
matched.sort(function (a, b) {
if (a.rank !== b.rank) return a.rank - b.rank;
// Inside the code tiers the shorter code is the better hit:
// "an" -> "an" before "ang" before "ang-x-...".
if (a.rank <= 1 && a.opt.foldCode.length !== b.opt.foldCode.length) {
return a.opt.foldCode.length - b.opt.foldCode.length;
}
// Otherwise keep the menu's own order (pinned languages first).
return a.i - b.i;
});
} else {
$.each(allOptions, function (i, o) {
if (!o.disabled) matched.push({ opt: o, rank: 0, i: i });
});
}
shown = $.map(matched.slice(0, 50), function (m) { return m.opt; });
if (!shown.length) {
closeList();
return;
}
$.each(shown, function (idx, opt) {
var li = $("<li role='option'></li>")
.attr("data-value", opt.value)
.attr("aria-selected", "false");
if (opt.tpl) {
// Template row: status dot, name, then two icons that stopPropagation
// so clicking them doesn't also select the option.
li.addClass("wiktku-ac-tplrow");
li.append($("<span class='wiktku-tpl-dot'></span>")
.addClass(opt.tplStatus === "yellow" ? "wiktku-tpl-dot-special" : "wiktku-tpl-dot-ok")
.attr("title", opt.tplStatus === "yellow"
? "Tenê ji bo rewşên taybet"
: "Tê pêşniyar kirin"));
li.append($("<span class='wiktku-ac-tpltext'></span>").text(opt.text));
var icons = $("<span class='wiktku-tpl-icons'></span>");
// touchend must be stopped here too, or a tap on either icon
// selects the template row instead of activating the icon.
var STOP = "mousedown touchstart touchend touchmove";
var link = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke")
.attr("href", tplPageUrl(opt.tpl));
// stopPropagation only -- preventDefault would stop the link from navigating.
link.on("click", function (e) { e.stopPropagation(); });
link.on(STOP, function (e) { e.stopPropagation(); });
var info = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
// preventDefault too (unlike the link) -- the info button has no
// navigation to preserve, so blocking the focus-shift keeps the
// list open instead of relying on the blur/re-render race.
info.on(STOP, function (e) { e.preventDefault(); e.stopPropagation(); });
info.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var handler = select.data("wiktkuTplInfo");
if (typeof handler === "function") handler(opt.tpl, wrapper);
});
icons.append(link).append(info);
li.append(icons);
} else {
li.text(opt.text);
}
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// mousedown (not click) so it beats the input's blur handler.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
li.on("mouseenter", function () {
// Don't scroll on hover -- it would shift the list under the pointer and cascade.
setActive(idx, /* scrollIntoView */ false);
});
resultsList.append(li);
});
positionList();
resultsList.show();
resultsList[0].scrollTop = 0;
textInput.attr("aria-expanded", "true");
}
function setActive(idx, scrollIntoView) {
var items = resultsList.children("li");
if (!items.length) return;
if (idx < 0) idx = items.length - 1;
if (idx >= items.length) idx = 0;
activeIndex = idx;
items.removeClass("wiktku-ac-active").attr("aria-selected", "false");
var li = items.eq(idx).addClass("wiktku-ac-active").attr("aria-selected", "true");
if (scrollIntoView === false) return;
// Keep the active item visible for keyboard navigation.
var ul = resultsList[0], el = li[0];
if (el.offsetTop < ul.scrollTop) {
ul.scrollTop = el.offsetTop;
} else if (el.offsetTop + el.offsetHeight > ul.scrollTop + ul.clientHeight) {
ul.scrollTop = el.offsetTop + el.offsetHeight - ul.clientHeight;
}
}
/** Puts the currently selected option's label back in the text box. */
function syncInputToSelect() {
var cur = select.val();
var match = null;
if (cur) {
$.each(allOptions, function (_, o) {
if (o.value === cur) { match = o; return false; }
});
}
textInput.val(match ? match.text : "");
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
closeList();
select.trigger("change");
}
textInput.on("input", function () {
showResults($(this).val());
});
textInput.on("focus", function () {
// Select the current label so typing replaces it, and open the full list
this.select();
showResults("");
});
textInput.on("click", function () {
if (!resultsList.is(":visible")) showResults($(this).val());
});
textInput.on("keydown", function (e) {
var open = resultsList.is(":visible");
switch (e.which) {
case 40: // Down
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(0); }
else setActive(activeIndex + 1);
break;
case 38: // Up
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(-1); }
else setActive(activeIndex - 1);
break;
case 13: // Enter -- must never submit the form while the list is open
if (open) {
e.preventDefault();
if (activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
} else if (shown.length === 1) {
selectOption(shown[0].value, shown[0].text);
}
}
break;
case 27: // Esc -- abandon the search, restore the real value
if (open) {
e.preventDefault();
closeList();
syncInputToSelect();
}
break;
case 9: // Tab -- commit the highlighted row on the way out
if (open && activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
}
closeList();
break;
}
});
textInput.on("blur", function () {
// Short delay so a touchstart/mousedown on a result fires first.
setTimeout(function () {
closeList();
// Half-typed text that never became a selection would otherwise sit
// in the box looking like a choice -- put the real value back.
syncInputToSelect();
}, 200);
});
// Re-caches the option list. Fired after the full language list loads, after
// options are enabled/disabled, and after programmatic .val() changes.
select.on("wiktku:refresh", function () {
cacheOptions();
syncInputToSelect();
if (resultsList.is(":visible")) showResults(textInput.val());
});
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
/** Gives every plain <select> under `root` the searchable picker; idempotent, and .wiktku-plain-select opts out. */
function initSelects(root) {
$(root).find("select").addBack("select").not(".wiktku-plain-select").each(function () {
var sel = $(this);
if (sel.prop("multiple")) return;
if (sel.closest(".wiktku-native-select-wrap").length) return; // already wrapped
var marker = $("<span></span>").insertBefore(sel);
marker.replaceWith(wrapNativeSelect(sel));
});
}
module.exports = {
capitalize: capitalize,
decapitalize: decapitalize,
escapeRegExp: escapeRegExp,
stripWikilinks: stripWikilinks,
splitTercimeIzah: splitTercimeIzah,
insertAtCursor: insertAtCursor,
foldDiacritics: foldDiacritics,
wrapNativeSelect: wrapNativeSelect,
initSelects: initSelects,
tplPageUrl: tplPageUrl,
makeManualResizable: wiktkuMakeManualResizable,
initManualResize: wiktkuInitManualResize
};
//</nowiki>
0mf4v2r8bo4hx8rgqo4y3wcgye7prn6
6334299
6334274
2026-08-17T10:31:00Z
Wikihez
7344
6334299
javascript
text/javascript
/**
* Generic text and UI helpers for the entry-creation wizard; no wizard-specific
* state, so any gadget can require("./start/util.js").
*/
//<nowiki>
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function splitTercimeIzah(mane) {
var m = /^([\s\S]*?)\s*\{\{îzah\|([\s\S]*)\}\}\s*$/.exec(mane || "");
return m ? { tercime: m[1], izah: m[2] } : null;
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
var FOLD_EXTRA ={
"ı":"i", "ø":"o","đ":"d","ð":"d","ə":"e","ħ":"h","ł":"l","ŋ":"n","ʉ":"u", "ɨ":"i","ʒ":"z","ɛ":"e","ɔ":"o","æ":"ae", "œ":"oe","ß":"ss","þ":"th"
};
function foldDiacritics(str) {
if (str === null || str === undefined) return "";
var s = String(str).toLowerCase();
if (typeof s.normalize === "function") {
// Split "î" into "i" + combining circumflex, then drop the accent.
s = s.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
return s.replace(/[^\u0000-\u007f]/g, function (ch) {
return Object.prototype.hasOwnProperty.call(FOLD_EXTRA, ch) ? FOLD_EXTRA[ch] : ch;
});
}
var WIKTKU_RESIZE_STORAGE_PREFIX = "wiktku_resize_height_";
function wiktkuResizeStorageKey(textarea) {
var cls = (textarea.attr("class") || "").trim().replace(/\s+/g, "_");
return WIKTKU_RESIZE_STORAGE_PREFIX + cls;
}
function wiktkuSaveResizeHeight(textarea, height) {
if (!(window.mw && mw.storage)) return;
try {
mw.storage.set(wiktkuResizeStorageKey(textarea), String(Math.round(height)));
} catch (e) { /* localStorage unavailable (private mode, quota, …) — skip caching */ }
}
function wiktkuLoadResizeHeight(textarea) {
if (!(window.mw && mw.storage)) return null;
try {
var v = mw.storage.get(wiktkuResizeStorageKey(textarea));
return v ? parseFloat(v) : null;
} catch (e) {
return null;
}
}
function wiktkuMakeManualResizable(textarea) {
textarea = $(textarea);
if (!textarea.is("textarea") || textarea.hasClass("wiktku-start-narrow")) return;
if (textarea.data("wiktkuResizableDone")) return;
textarea.data("wiktkuResizableDone", true);
textarea.wrap("<div class='wiktku-resize-wrap'></div>");
var grip = $("<div class='wiktku-resize-grip'></div>");
textarea.after(grip);
// Restore this field's last saved height, if any.
var cachedHeight = wiktkuLoadResizeHeight(textarea);
if (cachedHeight) {
var minHeightInit = parseFloat(textarea.css("min-height")) || 0;
textarea.css("height", Math.max(cachedHeight, minHeightInit) + "px");
}
var dragging = false, startY = 0, startHeight = 0;
function onPointerMove(e) {
if (!dragging) return;
var oe = e.originalEvent || e;
var minHeight = parseFloat(textarea.css("min-height")) || 0;
var newHeight = startHeight + (oe.clientY - startY);
textarea.css("height", Math.max(newHeight, minHeight) + "px");
}
function onPointerUp(e) {
if (!dragging) return;
dragging = false;
var oe = e.originalEvent || e;
try { grip[0].releasePointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).off("pointermove", onPointerMove);
$(document).off("pointerup pointercancel", onPointerUp);
wiktkuSaveResizeHeight(textarea, textarea.outerHeight());
}
grip.on("pointerdown", function (e) {
var oe = e.originalEvent || e;
if (oe.button !== undefined && oe.button !== 0) return; // primary contact only
e.preventDefault();
dragging = true;
startY = oe.clientY;
startHeight = textarea.outerHeight();
try { grip[0].setPointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).on("pointermove", onPointerMove);
$(document).on("pointerup pointercancel", onPointerUp);
});
}
/** Applies wiktkuMakeManualResizable to every eligible textarea in `context` (itself or its descendants). */
function wiktkuInitManualResize(context) {
var $context = $(context);
var $textareas = $context.is("textarea") ? $context : $context.find("textarea");
$textareas.each(function () {
wiktkuMakeManualResizable(this);
});
}
/** URL of a template's own wiki page, via mw.util when available. */
function tplPageUrl(name) {
var title = "Şablon:" + name;
if (window.mw && mw.util && typeof mw.util.getUrl === "function") {
return mw.util.getUrl(title);
}
return "/wiki/" + encodeURIComponent(title);
}
function wrapNativeSelect(select) {
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
if (select.prop("multiple")) {
return wrapper.append(select);
}
var placeholder = select.attr("data-placeholder") || "Lê bigere...";
select.hide();
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.attr("role", "combobox")
.attr("aria-autocomplete", "list")
.attr("aria-expanded", "false")
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results' role='listbox'></ul>").hide();
var allOptions = [];
var shown = [];
var activeIndex = -1;
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
var text = $(this).text();
// Skip a blank value with no label; a blank value WITH a label
// (e.g. "clear the selection") is still a real, selectable option.
if (!v && !text) return;
// Language options read "Name (code)" -- score the name alone so
// the trailing code doesn't also count as a name match.
var name = text.replace(/\s*\([^()]*\)\s*$/, "");
allOptions.push({
value: v,
text: text,
disabled: $(this).prop("disabled"),
// data-tpl options render as a template row (name, page link, info icon).
tpl: $(this).attr("data-tpl") || "",
tplStatus: $(this).attr("data-tpl-status") || "",
foldCode: foldDiacritics(v),
foldName: foldDiacritics(name),
foldText: foldDiacritics(text)
});
});
}
cacheOptions();
// True if `q` starts a word inside `name` ("kevn" in "ingiliziya kevn").
function startsWord(name, q) {
var from = 0, at;
while ((at = name.indexOf(q, from)) !== -1) {
if (at === 0 || /[\s\-\u2013\u2014(/,.]/.test(name.charAt(at - 1))) return true;
from = at + 1;
}
return false;
}
// Lower rank = better match; a code match always outranks a name match.
var RANK_NONE = 99;
function rankOption(o, q) {
var code = o.foldCode, name = o.foldName;
if (code === q) return 0; // exact code
if (code.indexOf(q) === 0) return 1; // code prefix
if (name === q) return 2; // exact name
if (name.indexOf(q) === 0) return 3; // name prefix
if (startsWord(name, q)) return 4; // starts a word in the name
if (name.indexOf(q) !== -1) return 5; // name substring
if (code.indexOf(q) !== -1) return 6; // code substring
if (o.foldText.indexOf(q) !== -1) return 7; // anywhere else in the label
return RANK_NONE;
}
function positionList() {
// Position the list above or below depending on viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px = 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
}
function closeList() {
resultsList.hide();
textInput.attr("aria-expanded", "false");
activeIndex = -1;
}
function showResults(query) {
var q = foldDiacritics($.trim(query || ""));
resultsList.empty();
activeIndex = -1;
var matched = [];
if (q) {
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var rank = rankOption(o, q);
if (rank !== RANK_NONE) matched.push({ opt: o, rank: rank, i: i });
});
if (!matched.length && q.indexOf(" ") !== -1) {
var tokens = q.split(/\s+/);
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var all = true;
$.each(tokens, function (_, t) {
if (o.foldText.indexOf(t) === -1) { all = false; return false; }
});
if (all) matched.push({ opt: o, rank: 8, i: i });
});
}
matched.sort(function (a, b) {
if (a.rank !== b.rank) return a.rank - b.rank;
// Inside the code tiers the shorter code is the better hit:
// "an" -> "an" before "ang" before "ang-x-...".
if (a.rank <= 1 && a.opt.foldCode.length !== b.opt.foldCode.length) {
return a.opt.foldCode.length - b.opt.foldCode.length;
}
// Otherwise keep the menu's own order (pinned languages first).
return a.i - b.i;
});
} else {
$.each(allOptions, function (i, o) {
if (!o.disabled) matched.push({ opt: o, rank: 0, i: i });
});
}
shown = $.map(matched.slice(0, 50), function (m) { return m.opt; });
if (!shown.length) {
closeList();
return;
}
$.each(shown, function (idx, opt) {
var li = $("<li role='option'></li>")
.attr("data-value", opt.value)
.attr("aria-selected", "false");
if (opt.tpl) {
// Template row: status dot, name, then two icons that stopPropagation
// so clicking them doesn't also select the option.
li.addClass("wiktku-ac-tplrow");
li.append($("<span class='wiktku-tpl-dot'></span>")
.addClass(opt.tplStatus === "yellow" ? "wiktku-tpl-dot-special" : "wiktku-tpl-dot-ok")
.attr("title", opt.tplStatus === "yellow"
? "Tenê ji bo rewşên taybet"
: "Tê pêşniyar kirin"));
li.append($("<span class='wiktku-ac-tpltext'></span>").text(opt.text));
var icons = $("<span class='wiktku-tpl-icons'></span>");
// touchend must be stopped here too, or a tap on either icon
// selects the template row instead of activating the icon.
var STOP = "mousedown touchstart touchend touchmove";
var link = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke")
.attr("href", tplPageUrl(opt.tpl));
// stopPropagation only -- preventDefault would stop the link from navigating.
link.on("click", function (e) { e.stopPropagation(); });
link.on(STOP, function (e) { e.stopPropagation(); });
var info = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
// mousedown: preventDefault too (unlike the link) -- the info
// button has no navigation to preserve, so blocking the
// focus-shift keeps the list open instead of relying on the
// blur/re-render race. touchstart/touchmove/touchend: stopPropagation
// only -- preventDefault on a touchstart suppresses the browser's
// synthetic click that follows it, which silently killed this
// button's own "click" handler below on mobile.
info.on("mousedown", function (e) { e.preventDefault(); e.stopPropagation(); });
info.on("touchstart touchend touchmove", function (e) { e.stopPropagation(); });
info.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var handler = select.data("wiktkuTplInfo");
if (typeof handler === "function") handler(opt.tpl, wrapper);
});
icons.append(link).append(info);
li.append(icons);
} else {
li.text(opt.text);
}
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// mousedown (not click) so it beats the input's blur handler.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
li.on("mouseenter", function () {
// Don't scroll on hover -- it would shift the list under the pointer and cascade.
setActive(idx, /* scrollIntoView */ false);
});
resultsList.append(li);
});
positionList();
resultsList.show();
resultsList[0].scrollTop = 0;
textInput.attr("aria-expanded", "true");
}
function setActive(idx, scrollIntoView) {
var items = resultsList.children("li");
if (!items.length) return;
if (idx < 0) idx = items.length - 1;
if (idx >= items.length) idx = 0;
activeIndex = idx;
items.removeClass("wiktku-ac-active").attr("aria-selected", "false");
var li = items.eq(idx).addClass("wiktku-ac-active").attr("aria-selected", "true");
if (scrollIntoView === false) return;
// Keep the active item visible for keyboard navigation.
var ul = resultsList[0], el = li[0];
if (el.offsetTop < ul.scrollTop) {
ul.scrollTop = el.offsetTop;
} else if (el.offsetTop + el.offsetHeight > ul.scrollTop + ul.clientHeight) {
ul.scrollTop = el.offsetTop + el.offsetHeight - ul.clientHeight;
}
}
/** Puts the currently selected option's label back in the text box. */
function syncInputToSelect() {
var cur = select.val();
var match = null;
if (cur) {
$.each(allOptions, function (_, o) {
if (o.value === cur) { match = o; return false; }
});
}
textInput.val(match ? match.text : "");
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
closeList();
select.trigger("change");
}
textInput.on("input", function () {
showResults($(this).val());
});
textInput.on("focus", function () {
// Select the current label so typing replaces it, and open the full list
this.select();
showResults("");
});
textInput.on("click", function () {
if (!resultsList.is(":visible")) showResults($(this).val());
});
textInput.on("keydown", function (e) {
var open = resultsList.is(":visible");
switch (e.which) {
case 40: // Down
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(0); }
else setActive(activeIndex + 1);
break;
case 38: // Up
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(-1); }
else setActive(activeIndex - 1);
break;
case 13: // Enter -- must never submit the form while the list is open
if (open) {
e.preventDefault();
if (activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
} else if (shown.length === 1) {
selectOption(shown[0].value, shown[0].text);
}
}
break;
case 27: // Esc -- abandon the search, restore the real value
if (open) {
e.preventDefault();
closeList();
syncInputToSelect();
}
break;
case 9: // Tab -- commit the highlighted row on the way out
if (open && activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
}
closeList();
break;
}
});
textInput.on("blur", function () {
// Short delay so a touchstart/mousedown on a result fires first.
setTimeout(function () {
closeList();
// Half-typed text that never became a selection would otherwise sit
// in the box looking like a choice -- put the real value back.
syncInputToSelect();
}, 200);
});
// Re-caches the option list. Fired after the full language list loads, after
// options are enabled/disabled, and after programmatic .val() changes.
select.on("wiktku:refresh", function () {
cacheOptions();
syncInputToSelect();
if (resultsList.is(":visible")) showResults(textInput.val());
});
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
/** Gives every plain <select> under `root` the searchable picker; idempotent, and .wiktku-plain-select opts out. */
function initSelects(root) {
$(root).find("select").addBack("select").not(".wiktku-plain-select").each(function () {
var sel = $(this);
if (sel.prop("multiple")) return;
if (sel.closest(".wiktku-native-select-wrap").length) return; // already wrapped
var marker = $("<span></span>").insertBefore(sel);
marker.replaceWith(wrapNativeSelect(sel));
});
}
module.exports = {
capitalize: capitalize,
decapitalize: decapitalize,
escapeRegExp: escapeRegExp,
stripWikilinks: stripWikilinks,
splitTercimeIzah: splitTercimeIzah,
insertAtCursor: insertAtCursor,
foldDiacritics: foldDiacritics,
wrapNativeSelect: wrapNativeSelect,
initSelects: initSelects,
tplPageUrl: tplPageUrl,
makeManualResizable: wiktkuMakeManualResizable,
initManualResize: wiktkuInitManualResize
};
//</nowiki>
naufznw2f62c3ubn1ul66sxyarzodhg
6334304
6334299
2026-08-17T11:49:56Z
Wikihez
7344
6334304
javascript
text/javascript
/**
* Generic text and UI helpers for the entry-creation wizard; no wizard-specific
* state, so any gadget can require("./start/util.js").
*/
//<nowiki>
function capitalize(str) {
if (!str) return str;
return str[0].toUpperCase() + str.substr(1);
}
function decapitalize(str) {
if (!str) return str;
return str[0].toLowerCase() + str.substr(1);
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function stripWikilinks(str) {
return str.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, function (m, target, display) {
return display || target;
});
}
function splitTercimeIzah(mane) {
var m = /^([\s\S]*?)\s*\{\{îzah\|([\s\S]*)\}\}\s*$/.exec(mane || "");
return m ? { tercime: m[1], izah: m[2] } : null;
}
function insertAtCursor(el, text) {
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
var pos = start + text.length;
el.selectionStart = el.selectionEnd = pos;
}
var FOLD_EXTRA ={
"ı":"i", "ø":"o","đ":"d","ð":"d","ə":"e","ħ":"h","ł":"l","ŋ":"n","ʉ":"u", "ɨ":"i","ʒ":"z","ɛ":"e","ɔ":"o","æ":"ae", "œ":"oe","ß":"ss","þ":"th"
};
function foldDiacritics(str) {
if (str === null || str === undefined) return "";
var s = String(str).toLowerCase();
if (typeof s.normalize === "function") {
// Split "î" into "i" + combining circumflex, then drop the accent.
s = s.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
return s.replace(/[^\u0000-\u007f]/g, function (ch) {
return Object.prototype.hasOwnProperty.call(FOLD_EXTRA, ch) ? FOLD_EXTRA[ch] : ch;
});
}
var WIKTKU_RESIZE_STORAGE_PREFIX = "wiktku_resize_height_";
function wiktkuResizeStorageKey(textarea) {
var cls = (textarea.attr("class") || "").trim().replace(/\s+/g, "_");
return WIKTKU_RESIZE_STORAGE_PREFIX + cls;
}
function wiktkuSaveResizeHeight(textarea, height) {
if (!(window.mw && mw.storage)) return;
try {
mw.storage.set(wiktkuResizeStorageKey(textarea), String(Math.round(height)));
} catch (e) { /* localStorage unavailable (private mode, quota, …) — skip caching */ }
}
function wiktkuLoadResizeHeight(textarea) {
if (!(window.mw && mw.storage)) return null;
try {
var v = mw.storage.get(wiktkuResizeStorageKey(textarea));
return v ? parseFloat(v) : null;
} catch (e) {
return null;
}
}
function wiktkuMakeManualResizable(textarea) {
textarea = $(textarea);
if (!textarea.is("textarea") || textarea.hasClass("wiktku-start-narrow")) return;
if (textarea.data("wiktkuResizableDone")) return;
textarea.data("wiktkuResizableDone", true);
textarea.wrap("<div class='wiktku-resize-wrap'></div>");
var grip = $("<div class='wiktku-resize-grip'></div>");
textarea.after(grip);
// Restore this field's last saved height, if any.
var cachedHeight = wiktkuLoadResizeHeight(textarea);
if (cachedHeight) {
var minHeightInit = parseFloat(textarea.css("min-height")) || 0;
textarea.css("height", Math.max(cachedHeight, minHeightInit) + "px");
}
var dragging = false, startY = 0, startHeight = 0;
function onPointerMove(e) {
if (!dragging) return;
var oe = e.originalEvent || e;
var minHeight = parseFloat(textarea.css("min-height")) || 0;
var newHeight = startHeight + (oe.clientY - startY);
textarea.css("height", Math.max(newHeight, minHeight) + "px");
}
function onPointerUp(e) {
if (!dragging) return;
dragging = false;
var oe = e.originalEvent || e;
try { grip[0].releasePointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).off("pointermove", onPointerMove);
$(document).off("pointerup pointercancel", onPointerUp);
wiktkuSaveResizeHeight(textarea, textarea.outerHeight());
}
grip.on("pointerdown", function (e) {
var oe = e.originalEvent || e;
if (oe.button !== undefined && oe.button !== 0) return; // primary contact only
e.preventDefault();
dragging = true;
startY = oe.clientY;
startHeight = textarea.outerHeight();
try { grip[0].setPointerCapture(oe.pointerId); } catch (err) { /* ignore */ }
$(document).on("pointermove", onPointerMove);
$(document).on("pointerup pointercancel", onPointerUp);
});
}
/** Applies wiktkuMakeManualResizable to every eligible textarea in `context` (itself or its descendants). */
function wiktkuInitManualResize(context) {
var $context = $(context);
var $textareas = $context.is("textarea") ? $context : $context.find("textarea");
$textareas.each(function () {
wiktkuMakeManualResizable(this);
});
}
/** URL of a template's own wiki page, via mw.util when available. */
function tplPageUrl(name) {
var title = "Şablon:" + name;
if (window.mw && mw.util && typeof mw.util.getUrl === "function") {
return mw.util.getUrl(title);
}
return "/wiki/" + encodeURIComponent(title);
}
function wrapNativeSelect(select) {
var wrapper = $("<span class='wiktku-native-select-wrap'></span>");
if (select.prop("multiple")) {
return wrapper.append(select);
}
var placeholder = select.attr("data-placeholder") || "Lê bigere...";
select.hide();
var textInput = $("<input type='search' autocomplete='off'>")
.attr("placeholder", placeholder)
.attr("role", "combobox")
.attr("aria-autocomplete", "list")
.attr("aria-expanded", "false")
.addClass("wiktku-start-narrow wiktku-ac-input");
var resultsList = $("<ul class='wiktku-ac-results' role='listbox'></ul>").hide();
var allOptions = [];
var shown = [];
var activeIndex = -1;
function cacheOptions() {
allOptions = [];
select.find("option").each(function () {
var v = $(this).val();
var text = $(this).text();
// Skip a blank value with no label; a blank value WITH a label
// (e.g. "clear the selection") is still a real, selectable option.
if (!v && !text) return;
// Language options read "Name (code)" -- score the name alone so
// the trailing code doesn't also count as a name match.
var name = text.replace(/\s*\([^()]*\)\s*$/, "");
allOptions.push({
value: v,
text: text,
disabled: $(this).prop("disabled"),
// data-tpl options render as a template row (name, page link, info icon).
tpl: $(this).attr("data-tpl") || "",
tplStatus: $(this).attr("data-tpl-status") || "",
foldCode: foldDiacritics(v),
foldName: foldDiacritics(name),
foldText: foldDiacritics(text)
});
});
}
cacheOptions();
// True if `q` starts a word inside `name` ("kevn" in "ingiliziya kevn").
function startsWord(name, q) {
var from = 0, at;
while ((at = name.indexOf(q, from)) !== -1) {
if (at === 0 || /[\s\-\u2013\u2014(/,.]/.test(name.charAt(at - 1))) return true;
from = at + 1;
}
return false;
}
// Lower rank = better match; a code match always outranks a name match.
var RANK_NONE = 99;
function rankOption(o, q) {
var code = o.foldCode, name = o.foldName;
if (code === q) return 0; // exact code
if (code.indexOf(q) === 0) return 1; // code prefix
if (name === q) return 2; // exact name
if (name.indexOf(q) === 0) return 3; // name prefix
if (startsWord(name, q)) return 4; // starts a word in the name
if (name.indexOf(q) !== -1) return 5; // name substring
if (code.indexOf(q) !== -1) return 6; // code substring
if (o.foldText.indexOf(q) !== -1) return 7; // anywhere else in the label
return RANK_NONE;
}
function positionList() {
// Position the list above or below depending on viewport space.
var inputRect = textInput[0].getBoundingClientRect();
var viewportH = window.innerHeight;
var spaceBelow = viewportH - inputRect.bottom - 8;
var spaceAbove = inputRect.top - 8;
var maxH = Math.min(224, Math.max(spaceBelow, spaceAbove)); // 224px = 14em
resultsList.css({
"max-height": maxH + "px",
"top": spaceBelow >= spaceAbove ? "100%" : "auto",
"bottom": spaceBelow >= spaceAbove ? "auto" : "100%"
});
}
function closeList() {
resultsList.hide();
textInput.attr("aria-expanded", "false");
activeIndex = -1;
}
function showResults(query) {
var q = foldDiacritics($.trim(query || ""));
resultsList.empty();
activeIndex = -1;
var matched = [];
if (q) {
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var rank = rankOption(o, q);
if (rank !== RANK_NONE) matched.push({ opt: o, rank: rank, i: i });
});
if (!matched.length && q.indexOf(" ") !== -1) {
var tokens = q.split(/\s+/);
$.each(allOptions, function (i, o) {
if (o.disabled) return;
var all = true;
$.each(tokens, function (_, t) {
if (o.foldText.indexOf(t) === -1) { all = false; return false; }
});
if (all) matched.push({ opt: o, rank: 8, i: i });
});
}
matched.sort(function (a, b) {
if (a.rank !== b.rank) return a.rank - b.rank;
// Inside the code tiers the shorter code is the better hit:
// "an" -> "an" before "ang" before "ang-x-...".
if (a.rank <= 1 && a.opt.foldCode.length !== b.opt.foldCode.length) {
return a.opt.foldCode.length - b.opt.foldCode.length;
}
// Otherwise keep the menu's own order (pinned languages first).
return a.i - b.i;
});
} else {
$.each(allOptions, function (i, o) {
if (!o.disabled) matched.push({ opt: o, rank: 0, i: i });
});
}
shown = $.map(matched.slice(0, 50), function (m) { return m.opt; });
if (!shown.length) {
closeList();
return;
}
$.each(shown, function (idx, opt) {
var li = $("<li role='option'></li>")
.attr("data-value", opt.value)
.attr("aria-selected", "false");
if (opt.tpl) {
// Template row: status dot, name, then two icons that stopPropagation
// so clicking them doesn't also select the option.
li.addClass("wiktku-ac-tplrow");
li.append($("<span class='wiktku-tpl-dot'></span>")
.addClass(opt.tplStatus === "yellow" ? "wiktku-tpl-dot-special" : "wiktku-tpl-dot-ok")
.attr("title", opt.tplStatus === "yellow"
? "Tenê ji bo rewşên taybet"
: "Tê pêşniyar kirin"));
li.append($("<span class='wiktku-ac-tpltext'></span>").text(opt.text));
var icons = $("<span class='wiktku-tpl-icons'></span>");
// touchend must be stopped here too, or a tap on either icon
// selects the template row instead of activating the icon.
var STOP = "mousedown touchstart touchend touchmove";
var link = $("<a class='wiktku-tpl-icon wiktku-tpl-icon-link' target='_blank' rel='noopener'>🔗</a>")
.attr("title", "Rûpela şablonê veke")
.attr("href", tplPageUrl(opt.tpl));
// stopPropagation only -- preventDefault would stop the link from navigating.
link.on("click", function (e) { e.stopPropagation(); });
link.on(STOP, function (e) { e.stopPropagation(); });
var info = $("<button type='button' class='wiktku-tpl-icon wiktku-tpl-icon-info'>i</button>")
.attr("title", "Ev şablon çi dike?");
// mousedown: preventDefault too (unlike the link) -- the info
// button has no navigation to preserve, so blocking the
// focus-shift stops the browser from blurring the search box
// on click, which would otherwise wipe out whatever the person
// had typed (the click handler below closes the list itself,
// deliberately, instead of relying on that blur). touchstart/
// touchmove/touchend: stopPropagation only -- preventDefault on
// a touchstart suppresses the browser's synthetic click that
// follows it, which silently killed this button's own "click"
// handler below on mobile.
info.on("mousedown", function (e) { e.preventDefault(); e.stopPropagation(); });
info.on("touchstart touchend touchmove", function (e) { e.stopPropagation(); });
info.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
// The info panel renders right after the picker as a normal
// document-flow sibling, but the results list is absolutely
// positioned and floats on top of whatever's below it while
// open -- so without this, the panel opens hidden underneath
// the still-open list instead of being visible.
closeList();
var handler = select.data("wiktkuTplInfo");
if (typeof handler === "function") handler(opt.tpl, wrapper);
});
icons.append(link).append(info);
li.append(icons);
} else {
li.text(opt.text);
}
// touchend (not touchstart) so a scroll doesn't trigger a select.
var touchMoved = false;
li.on("touchstart", function () {
touchMoved = false;
});
li.on("touchmove", function () {
touchMoved = true;
});
li.on("touchend", function (e) {
if (touchMoved) return; // was a scroll, not a tap
e.preventDefault();
selectOption(opt.value, opt.text);
});
// mousedown (not click) so it beats the input's blur handler.
li.on("mousedown", function (e) {
e.preventDefault();
selectOption(opt.value, opt.text);
});
li.on("mouseenter", function () {
// Don't scroll on hover -- it would shift the list under the pointer and cascade.
setActive(idx, /* scrollIntoView */ false);
});
resultsList.append(li);
});
positionList();
resultsList.show();
resultsList[0].scrollTop = 0;
textInput.attr("aria-expanded", "true");
}
function setActive(idx, scrollIntoView) {
var items = resultsList.children("li");
if (!items.length) return;
if (idx < 0) idx = items.length - 1;
if (idx >= items.length) idx = 0;
activeIndex = idx;
items.removeClass("wiktku-ac-active").attr("aria-selected", "false");
var li = items.eq(idx).addClass("wiktku-ac-active").attr("aria-selected", "true");
if (scrollIntoView === false) return;
// Keep the active item visible for keyboard navigation.
var ul = resultsList[0], el = li[0];
if (el.offsetTop < ul.scrollTop) {
ul.scrollTop = el.offsetTop;
} else if (el.offsetTop + el.offsetHeight > ul.scrollTop + ul.clientHeight) {
ul.scrollTop = el.offsetTop + el.offsetHeight - ul.clientHeight;
}
}
/** Puts the currently selected option's label back in the text box. */
function syncInputToSelect() {
var cur = select.val();
var match = null;
if (cur) {
$.each(allOptions, function (_, o) {
if (o.value === cur) { match = o; return false; }
});
}
textInput.val(match ? match.text : "");
}
function selectOption(value, text) {
textInput.val(text);
select.val(value);
closeList();
select.trigger("change");
}
textInput.on("input", function () {
showResults($(this).val());
});
textInput.on("focus", function () {
// Select the current label so typing replaces it, and open the full list
this.select();
showResults("");
});
textInput.on("click", function () {
if (!resultsList.is(":visible")) showResults($(this).val());
});
textInput.on("keydown", function (e) {
var open = resultsList.is(":visible");
switch (e.which) {
case 40: // Down
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(0); }
else setActive(activeIndex + 1);
break;
case 38: // Up
e.preventDefault();
if (!open) { showResults($(this).val()); setActive(-1); }
else setActive(activeIndex - 1);
break;
case 13: // Enter -- must never submit the form while the list is open
if (open) {
e.preventDefault();
if (activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
} else if (shown.length === 1) {
selectOption(shown[0].value, shown[0].text);
}
}
break;
case 27: // Esc -- abandon the search, restore the real value
if (open) {
e.preventDefault();
closeList();
syncInputToSelect();
}
break;
case 9: // Tab -- commit the highlighted row on the way out
if (open && activeIndex >= 0 && shown[activeIndex]) {
selectOption(shown[activeIndex].value, shown[activeIndex].text);
}
closeList();
break;
}
});
textInput.on("blur", function () {
// Short delay so a touchstart/mousedown on a result fires first.
setTimeout(function () {
closeList();
// Half-typed text that never became a selection would otherwise sit
// in the box looking like a choice -- put the real value back.
syncInputToSelect();
}, 200);
});
// Re-caches the option list. Fired after the full language list loads, after
// options are enabled/disabled, and after programmatic .val() changes.
select.on("wiktku:refresh", function () {
cacheOptions();
syncInputToSelect();
if (resultsList.is(":visible")) showResults(textInput.val());
});
wrapper.append(textInput).append(resultsList).append(select);
return wrapper;
}
/** Gives every plain <select> under `root` the searchable picker; idempotent, and .wiktku-plain-select opts out. */
function initSelects(root) {
$(root).find("select").addBack("select").not(".wiktku-plain-select").each(function () {
var sel = $(this);
if (sel.prop("multiple")) return;
if (sel.closest(".wiktku-native-select-wrap").length) return; // already wrapped
var marker = $("<span></span>").insertBefore(sel);
marker.replaceWith(wrapNativeSelect(sel));
});
}
module.exports = {
capitalize: capitalize,
decapitalize: decapitalize,
escapeRegExp: escapeRegExp,
stripWikilinks: stripWikilinks,
splitTercimeIzah: splitTercimeIzah,
insertAtCursor: insertAtCursor,
foldDiacritics: foldDiacritics,
wrapNativeSelect: wrapNativeSelect,
initSelects: initSelects,
tplPageUrl: tplPageUrl,
makeManualResizable: wiktkuMakeManualResizable,
initManualResize: wiktkuInitManualResize
};
//</nowiki>
le71moy9vwqavt0ft3zyet1hu18okkg
Modul:ckb-tewîn-nav
828
1358608
6334087
6334072
2026-08-16T12:05:10Z
Ghybu
1393
6334087
Scribunto
text/plain
-- Binêre [[Şablon:ckb-tewîn-nav]]
local export = {}
--[[
Sorani vowels:
-- Short vowels
["ە"] -- e
["و"] -- u
-- Long vowels
["ا"] -- a
["ێ"] -- ê
["ی"] -- î
["ۆ"] -- o
["وو"] -- û
-- Half-vowels
["و"] -- w
["ی"] -- y
--]]
local gsub = mw.ustring.gsub
local match = mw.ustring.match
function export.declination(noun)
local decl = {}
if not noun or noun == "" then
return decl
end
local lastLetter = mw.ustring.sub(noun, -1)
local lastTwo = mw.ustring.sub(noun, -2)
-- 1. NAVKÎ (Nominal)
decl.nomSg = noun
decl.nomPl = ""
-- 2. NEBINAVKIRÎ (Indefinite)
-- Yekjimar
if lastTwo == "وو" or match(lastLetter, "[اەێیۆ]") then
decl.indefSg = noun .. "یەک"
else
decl.indefSg = noun .. "ێک"
end
-- Pirjimar
if lastTwo == "وو" or match(lastLetter, "[اێۆ]") then
decl.indefPl = noun .. "یان"
elseif lastLetter == "ە" then
-- Remove the 'ە' and append 'ان'
local stem = gsub(noun, "ە$", "")
decl.indefPl = stem .. "ان"
else
decl.indefPl = noun .. "ان"
end
-- 3. BINAVKIRÎ (Definite)
-- Yekjimar
if match(lastLetter, "[ەاۆ]") then
decl.defSg = noun .. "کە"
--elseif match(lastLetter, "[وێی]") then
-- decl.defPl = noun .. "ەکە"
else
decl.defSg = noun .. "ەکە"
end
-- Pirjimar
if match(lastLetter, "[ەاۆ]") then
decl.defPl = noun .. "کان"
--elseif match(lastLetter, "[وێی]") then
-- decl.defPl = noun .. "ەکان"
else
decl.defPl = noun .. "ەکان"
end
-- 4. NÎŞANDER (Demonstrative)
-- Yekjimar
if match(lastLetter, "[اەێۆ]") then
decl.demonsSg = "ئەم " .. noun .. "یە"
else
decl.demonsSg = "ئەم " .. noun .. "ە"
end
-- Pirjimar
if lastLetter == "ا" then
decl.demonsPl = "ئەم " .. noun .. "یانە"
elseif lastLetter == "ە" then
-- Remove the 'ە' and append 'انە'
local stem = gsub(noun, "ە$", "")
decl.demonsPl = "ئەم " .. stem .. "انە"
else
decl.demonsPl = "ئەم " .. noun .. "انە"
end
return decl
end
return export
ln6pmpgantwc42u6bvocy7ttazsx09m
Kategorî:Parametreya ser a nehewce bi kurmancî
14
1358612
6334143
2026-08-16T17:21:18Z
Wikihez
7344
Bi riya [[Bikarhêner:Wikihez/skrîpt/js/addAutoCat.js/belge|JS]]: {{katbike}} (bêyî înterwîkî)
6334143
wikitext
text/x-wiki
{{katbike}}
1dpx16ue3vl1l6c1hkqu0q6ftpl2ezh
Kategorî:Parametreya ser a nehewce
14
1358613
6334145
2026-08-16T17:24:12Z
Wikihez
7344
Bi riya [[Bikarhêner:Wikihez/skrîpt/js/addAutoCat.js/belge|JS]]: {{katbike}} (bêyî înterwîkî)
6334145
wikitext
text/x-wiki
{{katbike}}
1dpx16ue3vl1l6c1hkqu0q6ftpl2ezh